1
0
Files
irix-657m-src/irix/kern/cell/relocation.c
2022-09-29 17:59:04 +03:00

1578 lines
41 KiB
C

/**************************************************************************
* *
* Copyright (C) 1994-1996 Silicon Graphics, Inc. *
* *
* These coded instructions, statements, and computer programs contain *
* unpublished proprietary information of Silicon Graphics, Inc., and *
* are protected by Federal copyright law. They may not be disclosed *
* to third parties or copied or duplicated in any form, in whole or *
* in part, without the prior written consent of Silicon Graphics, Inc. *
* *
**************************************************************************/
#ident "$Id: relocation.c,v 1.36 1997/10/02 20:32:39 cp Exp $"
#include <sys/types.h>
#include <sys/debug.h>
#include <sys/idbgentry.h>
#include <sys/errno.h>
#include <sys/kmem.h>
#include <sys/sema.h>
#include <sys/systm.h>
#include <ksys/cell/object.h>
#include <ksys/cell/relocation.h>
#include "relocation_private.h"
#include "invk_kore_stubs.h"
#include <ksys/cell/subsysid.h>
#include "I_kore_stubs.h"
#include <sys/ktrace.h>
#if DEBUG && OBJ_TRACE
struct ktrace *obj_trace = NULL;
cell_t obj_trace_id = -1;
static void idbg_obj_trace(__psint_t);
static void idbg_manifest(__psint_t);
#endif
struct obj_celldata *obj_celldatap;
/*
* Initialize a cell's object relocation engine - a service
* which directs the relocation of object servers between cells.
*/
void
obj_kore_init(void)
{
obj_celldatap = kern_calloc(1, sizeof(struct obj_celldata));
#if DEBUG && OBJ_TRACE
if (obj_trace == NULL) {
obj_trace = ktrace_alloc(10000, 0);
idbg_addfunc("objtrace", idbg_obj_trace);
idbg_addfunc("manifest", idbg_manifest);
}
#endif
mesg_handler_register(kore_msg_dispatcher, OBJECT_SUBSYSID);
}
/*
* Register an object manager for a given service as capable of
* performing object relocation using the supplied callout vector.
*/
void
obj_service_register(
service_t svc,
obj_service_if_t *obj_service_vector,
obj_relocation_if_t *obj_relocation_vector)
{
ASSERT(SERVICE_TO_CELL(svc) == cellid());
ASSERT(SERVICE_TO_SVCNUM(svc) < NUMSVCS);
obj_celldatap->obj_service_table[SERVICE_TO_SVCNUM(svc)] =
obj_service_vector;
obj_celldatap->obj_callout_table[SERVICE_TO_SVCNUM(svc)] =
obj_relocation_vector;
OBJ_TRACE6("obj_service_register", svc.s_intval,
"\n obj_service_vector", obj_service_vector,
"obj_relocation_vector", obj_relocation_vector);
}
/************************************************************************
* Object Management Functions *
* The following routines vector into the respective object manager *
* to request an object relocation function. *
************************************************************************/
int
obj_svr_lookup(
service_t svc,
void *id,
service_t *svr)
{
int svc_num = SERVICE_TO_SVCNUM(svc);
/* REFERENCED */
cell_t svc_cell = SERVICE_TO_CELL(svc);
obj_service_if_t *svcif;
ASSERT(svc_cell == cellid());
OBJ_TRACE4("obj_svr_lookup", svc.s_intval, "id", id);
svcif = obj_celldatap->obj_service_table[svc_num];
if (!svcif)
return EINVAL;
if (!svcif->obj_lookup)
return EPERM;
return (*(svcif->obj_lookup))(id, svr);
}
void
I_kore_svr_evict(
int svc_num,
void *id,
cell_t target,
int *error)
{
service_t obj_svc;
SERVICE_MAKE(obj_svc, cellid(), svc_num);
*error = obj_svr_evict(obj_svc, id, target);
}
int
obj_svr_evict(
service_t svc,
void *id,
cell_t target)
{
int svc_num = SERVICE_TO_SVCNUM(svc);
cell_t svc_cell = SERVICE_TO_CELL(svc);
obj_service_if_t *svcif;
int error;
OBJ_TRACE6("obj_svr_evict", svc.s_intval, "id", id, "target", target);
svcif = obj_celldatap->obj_service_table[svc_num];
if (!svcif)
return EINVAL;
if (!svcif->obj_evict)
return EPERM;
/*
* If necessary, function-ship to the server cell.
*/
if (svc_cell == cellid()) {
error = (*(svcif->obj_evict))(id, target);
} else {
/* REFERENCED */
int msgerr;
service_t kore_svc;
SERVICE_MAKE(kore_svc, svc_cell, SVC_OBJECT);
msgerr = invk_kore_svr_evict(kore_svc, svc_num, id, target,
&error);
ASSERT(!msgerr);
}
return error;
}
void
I_kore_svr_shutdown(
int svc_num,
cell_t surrogate,
int *error)
{
service_t obj_svc;
SERVICE_MAKE(obj_svc, cellid(), svc_num);
*error = obj_svr_shutdown(obj_svc, surrogate);
}
int
obj_svr_shutdown(
service_t svc,
cell_t surrogate)
{
int svc_num = SERVICE_TO_SVCNUM(svc);
cell_t svc_cell = SERVICE_TO_CELL(svc);
obj_service_if_t *svcif;
int error;
ASSERT(svc_cell == cellid());
OBJ_TRACE4("obj_svr_shutdown", svc.s_intval, "surrogate", surrogate);
svcif = obj_celldatap->obj_service_table[svc_num];
if (!svcif)
return EINVAL;
if (!svcif->obj_shutdown)
return EPERM;
/*
* If necessary, function-ship to the server cell.
*/
if (svc_cell == cellid()) {
error = (*(svcif->obj_shutdown))(surrogate);
} else {
/* REFERENCED */
int msgerr;
service_t kore_svc;
SERVICE_MAKE(kore_svc, svc_cell, SVC_OBJECT);
msgerr = invk_kore_svr_shutdown(kore_svc, svc_num,
surrogate, &error);
ASSERT(!msgerr);
}
return error;
}
/************************************************************************
* Object Lists *
* Object lists are object handle lists in the form of object bags. *
************************************************************************/
/*
* Create a list. The expected length dictates the initial block
* allocation for the underlying bag.
*/
obj_list_t
obj_list_create(
uint_t expected_length)
{
#ifdef DEBUG
uint_t entry_size = sizeof(obj_handle_t) + 2*sizeof(obj_tag_t);
#else
uint_t entry_size = sizeof(obj_handle_t) + sizeof(obj_tag_t);
#endif
return obj_bag_create(expected_length * entry_size);
}
/*
* Put a handle on (the end of) a list.
*/
void
obj_list_put(
obj_list_t list,
obj_handle_t *object)
{
/* REFERENCED */
int error;
error = obj_bag_put(list, OBJ_TAG_HANDLE,
(void *) object, sizeof(obj_handle_t));
ASSERT(!error);
}
/*
* Read next handle from a list.
*/
int
obj_list_get(
obj_list_t list,
obj_handle_t *object)
{
int error;
obj_bag_get_here(list, OBJ_TAG_HANDLE,
(void *) object, sizeof(obj_handle_t), error);
return error;
}
/************************************************************************
* Kernel Object Relocation Engine *
* Object manifests are employed to manage the relocation of a list of *
* objects from the local source cell to a target destination cell. A *
* series of call-outs are performed to the associated object managers *
* to load and unload object data into object bags to effect the *
* relocation of the set of objects from source to target cells. *
************************************************************************/
obj_bag_t
obj_mft_src_info(
obj_manifest_t *mftp)
{
ASSERT(mftp->sinfo);
return mftp->sinfo;
}
obj_bag_t
obj_mft_tgt_info(
obj_manifest_t *mftp)
{
ASSERT(mftp->tinfo);
return mftp->tinfo;
}
static void
obj_info_put(
obj_bag_t bagp,
obj_info_t *ip)
{
obj_tag_t tag = ip->tag;
obj_bag_size_t size = ip->info_size;
void *infop = ip->infop;
obj_bag_put(bagp, OBJ_TAG_HANDLE, &ip->hndl, sizeof(obj_handle_t));
if (tag != OBJ_TAG_NONE && infop != NULL && size != 0)
obj_bag_put(bagp, tag, infop, size);
else
obj_bag_put(bagp, OBJ_TAG_NONE, NULL, 0);
}
void
obj_mft_info_put(
obj_manifest_t *mftp,
obj_mft_info_t *mip)
{
ASSERT(mip);
ASSERT(mftp);
ASSERT(mftp->step == OBJ_SOURCE_PREPARE ||
mftp->step == OBJ_TARGET_PREPARE);
switch (mftp->step) {
case OBJ_SOURCE_PREPARE:
ASSERT(mftp->sinfo);
obj_info_put(mftp->sinfo, &mip->source);
obj_bag_put(mftp->sinfo, OBJ_TAG_RMODE,
&mip->rmode, sizeof(obj_rmode_t));
break;
case OBJ_TARGET_PREPARE:
ASSERT(mftp->tinfo);
obj_info_put(mftp->tinfo, &mip->target);
obj_bag_put(mftp->tinfo, OBJ_TAG_RMODE,
&mip->rmode, sizeof(obj_rmode_t));
break;
default:
panic("obj_mft_info_put: invalid call");
}
}
static void
obj_info_get(
obj_bag_t bagp,
obj_info_t *ip)
{
obj_tag_t tag = ip->tag;
obj_bag_size_t size = ip->info_size;
void *infop = ip->infop;
/* REFERENCED */
int error;
obj_bag_get_here(bagp, OBJ_TAG_HANDLE,
&ip->hndl, sizeof(obj_handle_t), error);
ASSERT(!error);
if (tag != OBJ_TAG_NONE && infop != NULL && size != 0) {
obj_bag_get_here(bagp, tag, infop, size, error);
if (error) {
/*
* If the expected tag wasn't gotten,
* continue but flag the info as null.
*/
ip->info_size = 0;
obj_bag_skip(bagp, OBJ_TAG_ANY);
}
} else
obj_bag_skip(bagp, OBJ_TAG_ANY);
}
void
obj_mft_info_get(
obj_manifest_t *mftp,
obj_mft_info_t *mip)
{
/* REFERENCED */
int error;
ASSERT(mip);
ASSERT(mftp);
ASSERT(mftp->step >= OBJ_SOURCE_PREPARE);
mip->source_cell = mftp->src;
mip->target_cell = mftp->tgt;
/*
* For the source prepare step, rmode is what
* obj_mft_put placed in the manifest structure.
*/
if (mftp->step == OBJ_SOURCE_PREPARE)
mip->rmode = mftp->rmode;
/*
* Return source info only after source prepare step.
*/
if (mftp->step > OBJ_SOURCE_PREPARE) {
ASSERT(mftp->sinfo);
obj_info_get(mftp->sinfo, &mip->source);
obj_bag_get_here(mftp->sinfo, OBJ_TAG_RMODE,
&mip->rmode, sizeof(obj_rmode_t), error);
ASSERT(!error);
}
/*
* Return target info only after target prepare step.
* Note: rmode flags updates that from source mft.
*/
if (mftp->step > OBJ_TARGET_PREPARE) {
ASSERT(mftp->tinfo);
obj_info_get(mftp->tinfo, &mip->target);
obj_bag_get_here(mftp->tinfo, OBJ_TAG_RMODE,
&mip->rmode, sizeof(obj_rmode_t), error);
ASSERT(!error);
}
}
void
obj_mft_src_info_add(
obj_manifest_t *mftp,
obj_tag_t tag,
void *info,
obj_bag_size_t info_size)
{
ASSERT(mftp->step == OBJ_SOURCE_PREPARE);
ASSERT(mftp->sinfo);
obj_bag_put(mftp->sinfo, tag, info, info_size);
}
static void
skip_to_tag(
obj_bag_t *bagp,
obj_tag_t tag_sought)
{
obj_tag_t tag;
obj_bag_size_t data_size;
while (obj_bag_peek(bagp, &tag, &data_size),
(tag != OBJ_TAG_EMPTY && tag != tag_sought))
obj_bag_skip(bagp, OBJ_TAG_ANY);
}
/*
* This routine discards the next object info for a given bag.
* The bag is expected to contain a handle followed additional
* info items.
*/
static void
skip_bag_info(
obj_bag_t *bagp)
{
/* REFERENCED */
int error;
error = obj_bag_skip(bagp, OBJ_TAG_HANDLE);
ASSERT(!error);
skip_to_tag(bagp, OBJ_TAG_HANDLE);
}
static void
obj_mft_sync(
obj_manifest_t *mftp)
{
if (mftp->sinfo)
skip_to_tag(mftp->sinfo, OBJ_TAG_HANDLE);
if (mftp->tinfo)
skip_to_tag(mftp->tinfo, OBJ_TAG_HANDLE);
}
static void
obj_mft_skip(
obj_manifest_t *mftp)
{
if (mftp->sinfo)
skip_bag_info(mftp->sinfo);
if (mftp->tinfo)
skip_bag_info(mftp->tinfo);
}
static void
obj_mft_rewind(
obj_manifest_t *mftp)
{
if (mftp->sinfo)
obj_bag_rewind(mftp->sinfo);
if (mftp->tinfo)
obj_bag_rewind(mftp->tinfo);
}
/*
* Create an object manifest for relocation to a given cell
* (from the local cell).
*/
obj_manifest_t *
obj_mft_create(
cell_t target)
{
obj_manifest_t *mftp;
mftp = kmem_alloc(sizeof(obj_manifest_t), KM_SLEEP);
ASSERT(mftp);
mftp->nobj = 0;
mftp->step = OBJ_SOURCE_PRELUDE;
mftp->src = cellid();
mftp->tgt = target;
mftp->obj_depth = 0;
mftp->obj_breadth = 0;
mftp->sinfo = mftp->tinfo = NULL;
OBJ_TRACE6("obj_mft_create", cellid(),
"target", target, "manifest", mftp);
return mftp;
}
/*
* Destroy a manifest after relocation.
*/
void
obj_mft_destroy(
obj_manifest_t *mftp)
{
OBJ_TRACE4("obj_mft_destroy", cellid(), "manifest", mftp);
if (mftp->sinfo)
obj_bag_destroy(mftp->sinfo);
if (mftp->tinfo)
obj_bag_destroy(mftp->tinfo);
kmem_free((void *) mftp, sizeof(obj_manifest_t));
}
/*
* Add an object (or object tree) to a manifest.
* This makes a callout to the associated subsystem (service)
* to prepare this object for relocation or export/import.
* This call indicates by relocation mode (rmode) whether there
* is a referencing object relocating to the target cell.
* The referenced virtual object may elect to perform a reference
* relocation (by export/import/unref) or to relocate the server if
* appropriate.
*/
int
obj_mft_put(
obj_manifest_t *src_manifest,
void *vobj,
int svc_num,
obj_rmode_t rmode)
{
int error = OBJ_SUCCESS;
obj_relocation_if_t *objif;
obj_kore_step_t last_step;
obj_tree_pos_t last_posn;
obj_rmode_t last_mode;
obj_bag_posn_t bag_posn;
ASSERT(src_manifest);
ASSERT(cellid() == src_manifest->src);
ASSERT(src_manifest->step == OBJ_SOURCE_PRELUDE ||
src_manifest->step == OBJ_SOURCE_PREPARE);
OBJ_TRACE10("obj_mft_put", cellid(), "manifest", src_manifest,
"\n vobj", vobj, "svc_num", svc_num, "rmode", rmode);
/*
* Allocate list/bags now if necessary.
*/
if (src_manifest->sinfo == NULL) {
src_manifest->sinfo = obj_bag_create(0);
}
/*
* Note the current position in the info bag first.
*/
(void) obj_bag_tell(src_manifest->sinfo, OBJ_BAG_MODE_PUT, &bag_posn);
/*
* Call into the subsystem to prepare the object for outbound
* relocation. Source information required by the target cell
* is added into the info bag attached to the manifest.
* The subsytem returns the object id of the prepared object
* - although this will be null if the object is being exported
* rather than relocated.
*/
objif = obj_celldatap->obj_callout_table[svc_num];
ASSERT(objif);
ASSERT(objif->obj_source_prepare);
OBJ_TRACE8("obj_mft_put", cellid(),
"\n vobj", vobj, "depth", src_manifest->obj_depth,
"breadth", src_manifest->obj_breadth);
/*
* Save current tree coordinates.
* Bump the depth coordinate as we descend into a nested object
* and reset the breadth count.
*/
last_posn = src_manifest->posn;
src_manifest->obj_depth++;
src_manifest->obj_breadth = 0;
last_mode = src_manifest->rmode;
src_manifest->rmode = rmode;
last_step = src_manifest->step;
src_manifest->step = OBJ_SOURCE_PREPARE;
/*
* Call into the relevant subsystem to have the object
* prepared for relocation (or to be exported).
*/
error = (*(objif->obj_source_prepare))(src_manifest,
vobj);
src_manifest->posn = last_posn;
src_manifest->step = last_step;
src_manifest->rmode = last_mode;
if (error) {
/* Truncate the info bag */
(void) obj_bag_seek(src_manifest->sinfo,
OBJ_BAG_MODE_PUT, &bag_posn);
OBJ_TRACE8("obj_mft_put", cellid(),
"manifest", src_manifest,
"\n vobj", vobj, "error", error);
return error;
}
/*
* Increment count and the breath coord to register this call.
*/
src_manifest->nobj++;
src_manifest->obj_breadth++;
OBJ_TRACE8("obj_mft_put", cellid(), "manifest", src_manifest,
"\n *(obj_source_prepare) vobj", vobj,
"nobj", src_manifest->nobj);
return error;
}
void kore_retarget_clients(obj_manifest_t *, bitmap_t *); /* forward */
void
kore_target_prepare(
cell_t target_cell, /* IN target cell */
int nobj, /* IN num objects to relocate */
obj_bag_t src_info, /* IN source info bag */
obj_bag_t *tgt_infop, /* OUT target info bag */
void **tgt_id, /* OUT target info id */
int *errorp) /* OUT return value */
{
service_t target_obj_svc;
obj_bag_header_t src_info_hdr;
obj_bag_vector_t *src_info_vecp;
size_t src_info_count;
obj_bag_header_t tgt_info_hdr;
obj_bag_vector_t *tgt_info_vecp = NULL;
size_t tgt_info_count = 0;
/* REFERENCED */
int msgerr;
/*
* For messaging we convert object bags to/from header&vector.
*/
obj_bag_to_message(src_info, &src_info_hdr,
&src_info_vecp, &src_info_count);
SERVICE_MAKE(target_obj_svc, target_cell, SVC_OBJECT);
msgerr = invk_kore_target_prepare(target_obj_svc, cellid(),
nobj,
&src_info_hdr,
(src_prep_invector_t *) src_info_vecp,
src_info_count,
&tgt_info_hdr,
(tgt_prep_outvector_t **) &tgt_info_vecp,
&tgt_info_count,
tgt_id,
errorp);
ASSERT(!msgerr);
if (*errorp == 0) {
*tgt_infop = obj_bag_from_message(
&tgt_info_hdr, tgt_info_vecp, tgt_info_count);
obj_bag_free_message(tgt_info_vecp, tgt_info_count);
}
obj_bag_free_message(src_info_vecp, src_info_count);
}
/*
* Ship the objects listed by a manifest (and now prepared for
* migration). This call results in callouts to have the (tree of)
* objects prepared on the target cell and subsequently bagged on
* the source cell and unbagged at the target.
*
* Note that an object tree is prepared top-down on source and
* target cells. That is, source information is gathered from the
* top of the tree down to leaf objects and embronic objects
* are created on the target cell in the same sequence. However,
* once prepared, bagging, transfer and unbagging is carried out
* bottom-up. That is, leaf objects are fully instantiated first.
*/
int
obj_mft_ship(
obj_manifest_t *src_manifest)
{
int error = OBJ_SUCCESS;
cell_t destination;
obj_bag_t src_info;
obj_handle_t src_hndl;
obj_tag_t hndl_tag = OBJ_TAG_HANDLE;
obj_bag_size_t hndl_size = sizeof(obj_handle_t);
service_t target_obj_svc;
obj_relocation_if_t *objif;
int svc_num;
obj_bag_t object_bag;
void *tgt_id;
int obj;
int nobj;
bitmap_t abt_bmp = NULL;
obj_bag_header_t bag_hdr;
obj_bag_vector_t *bag_vecp;
size_t bag_vec_count;
/* REFERENCED */
int msgerr;
ASSERT(src_manifest);
ASSERT(cellid() == src_manifest->src);
ASSERT(src_manifest->step == OBJ_SOURCE_PRELUDE);
src_info = src_manifest->sinfo;
ASSERT(src_info);
destination = src_manifest->tgt;
nobj = src_manifest->nobj;
SERVICE_MAKE(target_obj_svc, destination, SVC_OBJECT);
OBJ_TRACE8("obj_mft_ship", cellid(), "manifest", src_manifest,
"\n destination", destination, "src_info", src_info);
/*
* Called with a manifest of objects whose servers are
* local and whose behavior chain is already marked for
* outbound relocation.
*/
if (src_manifest->nobj == 0)
return OBJ_SUCCESS;
/*
* Call the target to prepare all objects.
* It returns its completed list containing target handles
* and target object info.
*/
kore_target_prepare(destination,
src_manifest->nobj,
src_manifest->sinfo,
&src_manifest->tinfo,
&tgt_id,
&error);
OBJ_TRACE6("obj_mft_ship", cellid(),
"tinfo", src_manifest->tinfo, "error", error);
ASSERT(!error);
/*
* Perform the client retargetting.
*/
src_manifest->step = OBJ_SOURCE_RETARGET;
kore_retarget_clients(src_manifest, &abt_bmp);
object_bag = obj_bag_create(0);
obj_mft_rewind(src_manifest);
src_manifest->step = OBJ_SOURCE_BAG;
for (obj = 0; obj < nobj; obj++) {
error = obj_bag_look(src_info, &hndl_tag, &src_hndl, &hndl_size);
ASSERT(!error);
svc_num = SERVICE_TO_SVCNUM(HANDLE_TO_SERVICE(src_hndl));
objif = obj_celldatap->obj_callout_table[svc_num];
if (abt_bmp && btst(abt_bmp, obj)) {
obj_bag_put(object_bag, OBJ_TAG_NONE, NULL, 0);
/* Skip over unused info */
obj_mft_skip(src_manifest);
continue;
}
ASSERT(objif->obj_source_bag);
error = (*(objif->obj_source_bag))(src_manifest,
object_bag);
OBJ_TRACE10("obj_mft_ship", cellid(), "bag", object_bag,
"\n *(obj_source_bag) svc_num", svc_num,
"objid", HANDLE_TO_OBJID(src_hndl),
"error", error);
if (error) {
/*
* Add a null entry to the object bag and
* remember the object id to be aborted.
* XXX - for compatibilty.
*/
obj_bag_put(object_bag, OBJ_TAG_NONE, NULL, 0);
if (!abt_bmp)
abt_bmp = bitmap_zalloc(nobj);
bset(abt_bmp, obj);
}
/* Discard any unread manifest info for this object */
obj_mft_sync(src_manifest);
}
/*
* Call the target to unbag all objects.
*/
obj_bag_to_message(object_bag, &bag_hdr, &bag_vecp, &bag_vec_count);
msgerr = invk_kore_target_unbag(target_obj_svc, cellid(),
tgt_id,
&bag_hdr,
(object_state_vec_invector_t *) bag_vecp,
bag_vec_count,
&error);
ASSERT(!msgerr);
OBJ_TRACE8("obj_mft_ship", cellid(), "manifest", src_manifest,
"\n invk_kore_target_unbag object_bag", object_bag,
"error", error);
ASSERT(!error);
obj_bag_free_message(bag_vecp, bag_vec_count);
obj_bag_destroy(object_bag);
/*
* Finally, clean up each source object.
* If the object's relocation was aborted, it will be
* flagged in the abort bitmap.
*/
obj_mft_rewind(src_manifest);
for (obj = 0; obj < nobj; obj++) {
/* REFERENCED */
objid_t src_objid;
error = obj_bag_look(src_info, &hndl_tag, &src_hndl, &hndl_size);
ASSERT(!error);
src_objid = HANDLE_TO_OBJID(src_hndl);
svc_num = SERVICE_TO_SVCNUM(HANDLE_TO_SERVICE(src_hndl));
objif = obj_celldatap->obj_callout_table[svc_num];
if (abt_bmp && btst(abt_bmp, obj)) {
ASSERT(objif->obj_source_abort);
src_manifest->step = OBJ_SOURCE_ABORT;
error = (*(objif->obj_source_abort))(src_manifest);
OBJ_TRACE8("obj_mft_ship", cellid(),
"\n *(obj_source_abort) svc_num", svc_num,
"objid", src_objid,
"error", error);
} else {
ASSERT(objif->obj_source_end);
src_manifest->step = OBJ_SOURCE_END;
error = (*(objif->obj_source_end))(src_manifest);
OBJ_TRACE10("obj_mft_ship", cellid(),
"manifest", src_manifest,
"\n *(obj_source_end) svc_num", svc_num,
"objid", src_objid,
"error", error);
}
ASSERT(!error);
/* Discard any unread manifest info for this object */
obj_mft_sync(src_manifest);
}
if (abt_bmp)
bitmap_free(abt_bmp, nobj);
return error;
}
/*
* Here on the target cell to prepare a set of objects
* to receive inbound relocation. A manifest is provided from the
* source cell to identify each object in an object-type specific
* manner. A bag is returned containing object-type specific data
* which may contain returning tokens and associated state.
*/
/* ARGSUSED */
void
I_kore_target_prepare(
cell_t source_cell, /* IN source cell */
int nobj, /* IN num objects */
obj_bag_header_t *src_info_hdrp, /* IN info header */
src_prep_invector_t *src_info_vecp, /* IN info vector */
size_t src_info_vec_count, /* IN info size */
obj_bag_header_t *tgt_info_hdrp, /* OUT header */
tgt_prep_outvector_t **tgt_infopp, /* OUT vector */
size_t *tgt_info_countp, /* OUT vector size */
void **tgt_id, /* OUT target info id */
int *errorp, /* OUT return value */
void **desc) /* OUT unused */
{
int error;
obj_manifest_t *tgt_manifest;
obj_tag_t tag;
obj_bag_size_t size;
void *v;
/*
* Create the manifest data structure to be passed down
* the object tree here on the target cell.
*/
tgt_manifest = obj_mft_create(cellid());
*tgt_id = (void *) tgt_manifest;
tgt_manifest->src = source_cell;
tgt_manifest->step = OBJ_TARGET_PREPARE;
tgt_manifest->tinfo = obj_bag_create(0);
tgt_manifest->sinfo = obj_bag_from_message(
src_info_hdrp,
(obj_bag_vector_t *) src_info_vecp,
src_info_vec_count);
OBJ_TRACE6("I_kore_target_prepare", cellid(),
"source_cell", source_cell,
"\n tgt_manifest", tgt_manifest);
/*
* We loop through each top-level object making a call to the
* relevant object subsystem to do the object-type-specific work.
*/
*errorp = 0;
while (obj_bag_peek(tgt_manifest->sinfo, &tag, &size),
tag == OBJ_TAG_HANDLE) {
error = obj_mft_get(tgt_manifest, &v);
if (error)
(*errorp)++;
}
ASSERT(tag == OBJ_TAG_EMPTY);
OBJ_TRACE6("I_kore_target_prepare", cellid(),
"\n tgt_manifest", tgt_manifest, "error", error);
ASSERT(tgt_manifest->nobj == nobj);
obj_bag_to_message(tgt_manifest->tinfo,
tgt_info_hdrp,
(obj_bag_vector_t **) tgt_infopp,
tgt_info_countp);
}
/* ARGSUSED */
void
I_kore_target_prepare_done(
tgt_prep_outvector_t *tgt_infop, /* IN vector */
size_t tgt_info_count, /* IN vector size */
void *desc) /* IN descriptor */
{
/* The vector (but not the bag) must be freed */
obj_bag_free_message((obj_bag_vector_t *) tgt_infop, tgt_info_count);
}
/*
* This routine is called on the target cell to prepare a tree of objects
* to receive inbound relocation. A manifest is provided from the
* source cell identifying each object in an object-type specific
* manner. This function defers the lookup and/or creation of
* objects by callout to the relevant system which is expected to
* returns a pointer the corresponding virtual object. This may
* in turn result in this function being called recursively to
* receive lower-level objects described in the manifest.
*/
int
obj_mft_get(
obj_manifest_t *tgt_manifest, /* IN target's object manifest */
void **vobj) /* OUT virtual object */
{
int error = OBJ_SUCCESS;
obj_relocation_if_t *objif;
int svc_num;
obj_tree_pos_t posn;
obj_tag_t hndl_tag = OBJ_TAG_HANDLE;
obj_handle_t hndl;
obj_bag_size_t hndl_size = sizeof(obj_handle_t);
ASSERT(tgt_manifest);
ASSERT(cellid() == tgt_manifest->tgt);
ASSERT(tgt_manifest->step == OBJ_TARGET_PREPARE);
/*
* The relevant service number is obtained from the source
* info in the manifest.
*/
error = obj_bag_look(tgt_manifest->sinfo, &hndl_tag, &hndl, &hndl_size);
ASSERT(!error);
svc_num = SERVICE_TO_SVCNUM(HANDLE_TO_SERVICE(hndl));
OBJ_TRACE10("obj_mft_get", cellid(),
"depth", tgt_manifest->obj_depth,
"breadth", tgt_manifest->obj_breadth,
"\n svc_num", svc_num,
"objid", HANDLE_TO_OBJID(hndl));
/*
* Save current tree coordinates.
* Bump the depth coordinate as we descend into a nested object
* and reset the breadth count.
*/
posn = tgt_manifest->posn;
tgt_manifest->obj_depth++;
tgt_manifest->obj_breadth = 0;
objif = obj_celldatap->obj_callout_table[svc_num];
ASSERT(objif);
ASSERT(objif->obj_target_prepare);
error = (*(objif->obj_target_prepare))(tgt_manifest,
vobj);
tgt_manifest->posn = posn;
OBJ_TRACE10("obj_mft_get", cellid(),
"depth", tgt_manifest->obj_depth,
"breadth", tgt_manifest->obj_breadth,
"\n *(obj_target_prepare) *vobj", *vobj, "error", error);
ASSERT(!error); /* XXX - what if not? */
tgt_manifest->nobj++;
/*
* Increment the breath component to register this call.
*/
tgt_manifest->obj_breadth++;
return error;
}
/*
* This server routine is invoked on the target cell to unbag object
* data transferred from the source cell. This bagged data
* corresponds with a manifest of previously prepared objects.
* Unbagging consists of looping through the manifest and
* calling-out to the relevant subsystem to do the dirty work.
*/
/* ARGSUSED */
void
I_kore_target_unbag(
cell_t source_cell, /* IN source cell */
void *tgt_id, /* IN target manifest id */
obj_bag_header_t *object_bag_hdrp,
object_state_vec_invector_t *object_bag_vecp,
size_t object_bag_vec_count,
int *errorp) /* OUT return value */
{
obj_manifest_t *tgt_manifest;
obj_handle_t tgt_hndl;
obj_relocation_if_t *objif;
obj_bag_t tgt_info;
obj_tag_t hndl_tag = OBJ_TAG_HANDLE;
obj_bag_size_t hndl_size = sizeof(obj_handle_t);
obj_bag_t object_bag;
int svc_num;
int obj;
int nobj;
/* REFERENCED */
int error;
tgt_manifest = (obj_manifest_t *) tgt_id;
ASSERT(tgt_manifest->step == OBJ_TARGET_PREPARE);
nobj = tgt_manifest->nobj;
tgt_info = tgt_manifest->tinfo;
object_bag = obj_bag_from_message(object_bag_hdrp,
(obj_bag_vector_t *) object_bag_vecp,
object_bag_vec_count);
OBJ_TRACE8("I_kore_target_unbag", cellid(), "source_cell", source_cell,
"\n bag", object_bag, "tinfo", tgt_info);
/*
* Here on the target cell to unbag the set of objects.
*
* We loop through each object making a call to the unbag object
* specific routine to do the object-type-specific work.
* Note that a null item (OBJ_TAG_NONE) in the bag results in a
* call to the abort callout.
*/
obj_mft_rewind(tgt_manifest);
for (obj = 0; obj < nobj; obj++) {
error = obj_bag_look(tgt_info, &hndl_tag, &tgt_hndl, &hndl_size);
ASSERT(!error);
svc_num = SERVICE_TO_SVCNUM(HANDLE_TO_SERVICE(tgt_hndl));
objif = obj_celldatap->obj_callout_table[svc_num];
ASSERT(objif);
if (obj_bag_skip(object_bag, OBJ_TAG_NONE) == OBJ_BAG_ERR_TAG) {
ASSERT(objif->obj_target_unbag);
tgt_manifest->step = OBJ_TARGET_UNBAG;
error = (*(objif->obj_target_unbag))(
tgt_manifest,
object_bag);
OBJ_TRACE10("I_kore_target_unbag", cellid(),
"bag", object_bag,
"\n *(obj_target_unbag) svc_num", svc_num,
"objid", HANDLE_TO_OBJID(tgt_hndl),
"error", error);
ASSERT(!error);
} else {
/* REFERENCED */
ASSERT(objif->obj_target_abort);
tgt_manifest->step = OBJ_TARGET_ABORT;
(void) (*(objif->obj_target_abort))(tgt_manifest);
OBJ_TRACE8("I_kore_target_unbag", cellid(),
"bag", object_bag,
"\n *(obj_target_abort) svc_num", svc_num,
"objid", HANDLE_TO_OBJID(tgt_hndl));
}
/* Discard any unread manifest info for this object */
obj_mft_sync(tgt_manifest);
}
*errorp = 0;
obj_mft_destroy(tgt_manifest);
obj_bag_destroy(object_bag);
OBJ_TRACE4("I_kore_target_unbag", cellid(),
"*errorp", *errorp);
}
/************************************************************************
* Object Retargetting Support *
* The following routines are used to track the retargetting of client *
* cells as the source server redirects all its clients to the parpared *
* target cell. *
************************************************************************/
/*
* Request commencement of object migration. Permission is
* Initialize a synchronizing control structure.
*/
void
obj_retarget_begin(
obj_retarget_t *rt)
{
obj_retarget_state_t *rtp;
rtp = (obj_retarget_state_t *) kmem_alloc(sizeof(obj_retarget_state_t),
KM_SLEEP);
ASSERT(rtp);
spinlock_init(&rtp->rt_lock, "rt_sync");
sv_init(&rtp->rt_sync, SV_DEFAULT, "rt_sync");
/* One for the pot to avoid premature triggering */
rtp->rt_count = 1;
*rt = (obj_retarget_t) rtp;
}
/*
* Marks the end. All clients are have retargetted and we deallocate
* the control structure.
*/
void
obj_retarget_end(
obj_retarget_t *rt)
{
obj_retarget_state_t *rtp;
ASSERT(rt);
rtp = (obj_retarget_state_t *) *rt;
ASSERT(rtp);
ASSERT(rtp->rt_count == 0);
spinlock_destroy(&rtp->rt_lock);
sv_destroy(&rtp->rt_sync);
kmem_free((void *) rtp, sizeof(obj_retarget_t));
*rt = NULL;
}
/*
* Increment a count of the number of client cells being retargetted.
*/
/* ARGSUSED */
void
obj_retarget_register(
obj_retarget_t *rt,
cell_t client)
{
obj_retarget_state_t *rtp;
int s;
ASSERT(rt);
rtp = (obj_retarget_state_t *) *rt;
ASSERT(rtp);
s = mutex_spinlock(&rtp->rt_lock);
rtp->rt_count++;
mutex_spinunlock(&rtp->rt_lock, s);
}
/*
* Decrement the count of the number of client cells being retargetted.
* When there are none, signal this to anyone waiting.
*/
/* ARGSUSED */
void
obj_retarget_deregister(
obj_retarget_t *rt,
cell_t client)
{
obj_retarget_state_t *rtp;
int s;
ASSERT(rt);
rtp = (obj_retarget_state_t *) *rt;
ASSERT(rtp);
ASSERT(rtp->rt_count > 0);
s = mutex_spinlock(&rtp->rt_lock);
if (--(rtp->rt_count) == 0)
sv_signal(&rtp->rt_sync);
mutex_spinunlock(&rtp->rt_lock, s);
}
/*
* Wait for all registered clients to deregister.
*/
void
obj_retarget_wait(
obj_retarget_t *rt)
{
obj_retarget_state_t *rtp;
int s;
ASSERT(rt);
rtp = (obj_retarget_state_t *) *rt;
ASSERT(rtp);
ASSERT(rtp->rt_count > 0);
s = mutex_spinlock(&rtp->rt_lock);
rtp->rt_count--; /* Remove the one for the pot */
while (rtp->rt_count > 0) {
sv_wait(&rtp->rt_sync, PZERO, &rtp->rt_lock, s);
s = mutex_spinlock(&rtp->rt_lock);
}
mutex_spinunlock(&rtp->rt_lock, s);
}
void
I_kore_client_retargetted(
cell_t client, /* IN who's retargetted */
int nobj, /* IN number of objects */
bitmap_t errobj_bmp, /* IN objects not found */
size_t errobj_bmp_sz, /* IN object bitmap size */
obj_retarget_t rt) /* IN synchronization object */
{
int obj;
obj_retarget_state_t *rtsp;
obj_manifest_t *src_manifest;
tks_state_t **tkstate;
obj_bag_header_t src_hdr;
obj_bag_vector_t *src_vecp;
size_t src_vec_count;
obj_bag_header_t tgt_hdr;
obj_bag_vector_t *tgt_vecp;
size_t tgt_vec_count;
rtsp = (obj_retarget_state_t *) rt;
src_manifest = (obj_manifest_t *) rtsp->rt_data1;
tkstate = (tks_state_t **) rtsp->rt_data2;
OBJ_TRACE10("I_kore_client_retargetted", cellid(), "client", client,
"\n nobj", nobj, "errobj_bmp", errobj_bmp,
"src_manifest", src_manifest);
/*
* The job's done if there were no errore.
*/
if (errobj_bmp == NULL) {
obj_retarget_deregister(&rt, client);
return;
}
ASSERT(src_manifest);
ASSERT(tkstate);
/*
* Otherwise we have to re-check whether the erring objects
* still exist on this cell. We try again if any remains
* - we presume there was a race in which the retarget
* overtook a grant and the new client hadn't been created.
*/
for (obj = 0; obj < nobj; obj++) {
if (btst(errobj_bmp, obj)) {
ASSERT(tkstate[obj]);
if (!tks_isclient(tkstate[obj], client))
bclr(errobj_bmp, obj);
}
}
if (bitmap_isnull(errobj_bmp, nobj)) {
/* No unfound object still exists - we're done */
obj_retarget_deregister(&rt, client);
} else {
service_t client_obj_svc;
/* REFERENCED */
int msgerr;
/*
* Try again...
*/
OBJ_TRACE10("I_kore_client_retargetted", CELL_LOCAL,
"invk_kore_client_retarget cell", client,
"\n sinfo", src_manifest->sinfo,
"tinfo", src_manifest->tinfo,
"errobj_bmp", errobj_bmp);
obj_bag_to_message(src_manifest->sinfo,
&src_hdr, &src_vecp, &src_vec_count);
obj_bag_to_message(src_manifest->tinfo,
&tgt_hdr, &tgt_vecp, &tgt_vec_count);
SERVICE_MAKE(client_obj_svc, client, SVC_OBJECT);
msgerr = invk_kore_client_retarget(
client_obj_svc, CELL_LOCAL,
src_manifest->tgt, src_manifest->nobj,
&src_hdr,
(src_rt_invector_t *) src_vecp, src_vec_count,
&tgt_hdr,
(tgt_rt_invector_t *) tgt_vecp, tgt_vec_count,
errobj_bmp, errobj_bmp_sz, rt);
ASSERT(!msgerr);
obj_bag_free_message(src_vecp, src_vec_count);
obj_bag_free_message(tgt_vecp, tgt_vec_count);
}
}
void
I_kore_client_retarget(
cell_t source_cell, /* IN source server cell */
cell_t target_cell, /* IN target server cell */
int nobj, /* IN num objects to retarget */
obj_bag_header_t *src_hdrp, /* IN tgt info header */
src_rt_invector_t *src_vecp, /* IN tgt info vector */
size_t src_vec_count, /* IN tgt info size */
obj_bag_header_t *tgt_hdrp, /* IN tgt info header */
tgt_rt_invector_t *tgt_vecp, /* IN tgt info vector */
size_t tgt_vec_count, /* IN tgt info size */
bitmap_t object_bmp, /* IN which objects */
size_t object_bmp_sz, /* IN object bitmap size */
obj_retarget_t rt) /* IN synchronization object */
{
obj_manifest_t *mftp;
service_t source_obj_svc;
obj_relocation_if_t *objif;
bitmap_t errobj_bmp = NULL;
obj_tag_t hndl_tag = OBJ_TAG_HANDLE;
obj_bag_size_t hndl_size = sizeof(obj_handle_t);
obj_handle_t hndl;
int svc_num;
int obj;
int error;
/* REFERENCED */
int msgerr;
mftp = obj_mft_create(cellid());
mftp->src = source_cell;
mftp->tgt = target_cell;
mftp->step = OBJ_CLIENT_RETARGET;
mftp->sinfo = obj_bag_from_message(src_hdrp,
(obj_bag_vector_t *) src_vecp,
src_vec_count);
mftp->tinfo = obj_bag_from_message(tgt_hdrp,
(obj_bag_vector_t *) tgt_vecp,
tgt_vec_count);
OBJ_TRACE10("I_kore_client_retarget", cellid(),
"source", source_cell, "sinfo bag", mftp->sinfo,
"\n tinfo bag", mftp->tinfo, "object_bmp", object_bmp);
obj_mft_rewind(mftp);
for (obj = 0; obj < nobj; obj++) {
error = obj_bag_look(mftp->tinfo, &hndl_tag, &hndl, &hndl_size);
ASSERT(!error);
svc_num = SERVICE_TO_SVCNUM(HANDLE_TO_SERVICE(hndl));
if (btst(object_bmp, obj)) {
objif = obj_celldatap->obj_callout_table[svc_num];
ASSERT(objif);
ASSERT(objif->obj_client_retarget);
error = (*(objif->obj_client_retarget))(mftp);
OBJ_TRACE10("I_kore_client_retarget", cellid(),
"obj", obj,
" \n *(obj_client_retarget) svc_num",
svc_num,
"objid", HANDLE_TO_OBJID(hndl),
"error", error);
if (error) {
if (!errobj_bmp)
errobj_bmp = bitmap_zalloc(nobj);
bset(errobj_bmp, obj);
}
} else {
/* Skip over unused info */
obj_mft_skip(mftp);
}
}
SERVICE_MAKE(source_obj_svc, source_cell, SVC_OBJECT);
ASSERT(bitmap_size(nobj) == object_bmp_sz);
msgerr = invk_kore_client_retargetted(source_obj_svc, CELL_LOCAL, nobj,
errobj_bmp, object_bmp_sz, rt);
ASSERT(!msgerr);
if (errobj_bmp)
bitmap_free(errobj_bmp, nobj);
obj_mft_destroy(mftp);
}
void
kore_retarget_clients(
obj_manifest_t *src_manifest, /* IN manifest */
bitmap_t *abt_bmpp) /* OUT abort bitmap */
{
int error = OBJ_SUCCESS;
cell_t source;
cell_t target;
obj_handle_t src_hndl;
obj_tag_t hndl_tag = OBJ_TAG_HANDLE;
obj_bag_size_t hndl_size = sizeof(obj_handle_t);
obj_relocation_if_t *objif;
int svc_num;
obj_bag_t tgt_info;
obj_bag_t src_info;
int nobj;
int obj;
int cell;
tks_state_t **tkstate;
obj_retarget_t rt;
obj_bag_header_t src_hdr;
obj_bag_vector_t *src_vecp;
size_t src_vec_count;
obj_bag_header_t tgt_hdr;
obj_bag_vector_t *tgt_vecp;
size_t tgt_vec_count;
ASSERT(src_manifest);
ASSERT(cellid() == src_manifest->src);
tgt_info = src_manifest->tinfo;
src_info = src_manifest->sinfo;
ASSERT(src_info);
ASSERT(tgt_info);
source = src_manifest->src;
target = src_manifest->tgt;
nobj = src_manifest->nobj;
OBJ_TRACE8("kore_retarget_clients", cellid(), "manifest", src_manifest,
"\n target", target, "src_info", src_info);
/*
* Allocate token state pointers for each object.
*/
tkstate = (tks_state_t **) kmem_zalloc(nobj*sizeof(tks_state_t *),
KM_SLEEP);
obj_retarget_begin(&rt);
((obj_retarget_state_t *) rt)->rt_data1 = (void *) src_manifest;
((obj_retarget_state_t *) rt)->rt_data2 = (void *) tkstate;
obj_mft_rewind(src_manifest);
ASSERT(*abt_bmpp == NULL);
for (obj = 0; obj < nobj; obj++) {
error = obj_bag_look(src_info, &hndl_tag, &src_hndl, &hndl_size);
ASSERT(!error);
svc_num = SERVICE_TO_SVCNUM(HANDLE_TO_SERVICE(src_hndl));
objif = obj_celldatap->obj_callout_table[svc_num];
if (objif->obj_source_retarget != NULL) {
error = (*(objif->obj_source_retarget))(
src_manifest,
&tkstate[obj]);
OBJ_TRACE10("kore_retarget_clients", cellid(),
"tks_state", tkstate[obj],
"\n *(obj_source_retarget) svc_num", svc_num,
"objid", HANDLE_TO_OBJID(src_hndl),
"error", error);
if (error) {
if (*abt_bmpp == NULL)
*abt_bmpp = bitmap_zalloc(nobj);
bset(*abt_bmpp, obj);
}
/*
* Toss (any further) details for this object
* to get to next handle in manifest.
*/
obj_mft_sync(src_manifest);
} else {
/*
* Toss all details for this object
* to get to next handle in manifest.
*/
obj_mft_skip(src_manifest);
}
}
/*
* All source servers are now quiesced and we have the token state
* for each. For each remote cell with any client object,
* request the retarget.
* What we need to intuit first is which cells we need to visit.
* And for each cell we do visit, which client objects are there.
*/
obj_bag_to_message(src_info, &src_hdr, &src_vecp, &src_vec_count);
obj_bag_to_message(tgt_info, &tgt_hdr, &tgt_vecp, &tgt_vec_count);
for (cell = 0; cell < MAX_CELLS; cell++) {
bitmap_t object_bmp;
service_t client_obj_svc;
if (cell == source || cell == target)
continue;
object_bmp = bitmap_zalloc(nobj);
for (obj = 0; obj < nobj; obj++) {
if (tkstate[obj] && tks_isclient(tkstate[obj], cell)) {
bset(object_bmp, obj);
}
}
if (!bitmap_isnull(object_bmp, nobj)) {
/* REFERENCED */
int msgerr;
OBJ_TRACE10("kore_retarget_clients", CELL_LOCAL,
"invk_kore_client_retarget cell", cell,
"\n sinfo", src_info,
"tinfo", tgt_info,
"object_bmp", object_bmp);
obj_retarget_register(&rt, cell);
SERVICE_MAKE(client_obj_svc, cell, SVC_OBJECT);
msgerr = invk_kore_client_retarget(
client_obj_svc, CELL_LOCAL,
target, nobj,
&src_hdr,
(src_rt_invector_t *) src_vecp, src_vec_count,
&tgt_hdr,
(tgt_rt_invector_t *) tgt_vecp, tgt_vec_count,
object_bmp, bitmap_size(nobj),
rt);
ASSERT(!msgerr);
}
bitmap_free(object_bmp, nobj);
}
obj_bag_free_message(src_vecp, src_vec_count);
obj_bag_free_message(tgt_vecp, tgt_vec_count);
OBJ_TRACE4("kore_retarget_clients", cellid(), "waiting, rt", rt);
obj_retarget_wait(&rt);
/*
* Deallocate stuff.
*/
obj_retarget_end(&rt);
kmem_free((void *) tkstate, nobj*sizeof(char *));
}
#if DEBUG && OBJ_TRACE
static void
idbg_obj_trace(__psint_t x)
{
__psint_t id;
int idx;
int count;
if (x == -1) {
qprintf("Displaying all entries\n");
idx = -1;
count = 0;
} else if (x < 0) {
idx = -1;
count = (int)-x;
qprintf("Displaying last %d entries\n", count);
} else {
qprintf("Displaying entries for cell %d\n", x);
idx = 1;
id = x;
count = 0;
}
ktrace_print_buffer(obj_trace, id, idx, count);
}
void
idbg_manifest(__psint_t x)
{
obj_manifest_t *mftp;
uint_t step;
char *step_name[] = {
"OBJ_SOURCE_PRELUDE",
"OBJ_SOURCE_PREPARE",
"OBJ_TARGET_PREPARE",
"OBJ_SOURCE_RETARGET",
"OBJ_CLIENT_RETARGET",
"OBJ_SOURCE_BAG",
"OBJ_TARGET_UNBAG",
"OBJ_SOURCE_END",
"OBJ_SOURCE_ABORT",
"OBJ_TARGET_ABORT"
};
mftp = (obj_manifest_t *) x;
step = mftp->step;
qprintf("Manifest @0x%x: source %d target %d step %s\n",
mftp, mftp->src, mftp->tgt,
step <= OBJ_TARGET_ABORT ? step_name[mftp->step] : "??");
qprintf(" nobj %d, depth %d breadth %d\n",
mftp->nobj, mftp->posn.depth, mftp->posn.breadth);
qprintf(" sinfo bag 0x%x; tinfo bag 0x%x\n",
mftp->sinfo, mftp->tinfo);
}
#endif