/*
* 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 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <meta.h>
#include <assert.h>
#include <ctype.h>
#include <mdiox.h>
#include <meta.h>
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <strings.h>
/*
* Design Notes:
*
* All of the code in this file supports the addition of metastat -c output
* for the verbose option of metaimport. Some of this code is also used by
* the command metastat for concise output(cmd/lvm/util/metastat.c).
* The code is designed to produce the same output as metastat -c does for a
* given diskset--with a couple exceptions.
* The primary differences between the output for the metastat -c command and
* metastat output for metaimport -v are:
* - the set name is not printed next to each metadevice
* - top-level state information is not printed for some metadevices
* - the percent that a disk has completed resyncing is not listed
* in metaimport -v.
*
*
* The general layout of this file is as follows:
*
* - report_metastat_info()
* This is the primary entry point for the functions in this file, with
* the exception of several functions that are also called from
* cmd/io/lvm/util/metastat.c
* report_metastat_info() calls functions to read in all the the
* Directory blocks and Record blocks and then process the information
* needed to print out the metadevice records in the same format as
* metastat -c.
*
* - read_all_mdrecords()
* Reads in all the Directory blocks in the diskset and verifies their
* validity. For each Directly block, it loops through all Directory
* Entries and for each one that contains a metadevice record calls
* read_md_record(). Because the output is designed to imitate the
* output of metastat -c, we ignore metadevice records for
* optimized resync, changelog, and translog.
*
* - read_md_record()
* Reads in a Directory Entry and its associated Record block. The
* revision information for the Record block is checked and it is
* determined whether or not it is a 64bit Record block or a 32bit record
* block. For each valid Record block, it allocates an md_im_rec_t
* structure and calls extract_mduser_data().
*
* - extract_mduser_data()
* Populates the md_im_rec_t data structure with information about the
* record's associated metadevice. Also, the name of the metadevice is
* either copied from the NM namespace(if it exists there) or is generated
* from the record's un_self_id.
*
* - process_toplevel_devices()
* For a given metadevice type, searchs through the md_im_rec_t **mdimpp,
* list of all metadevices in the set, to find all records of the
* specified type that do not have a parent and puts them on a temp list.
* The temp list is then iterated through and the associated processing
* function is called.
*
* - process_(trans, hotspare, hotspare_pool, soft_part, mirror, stripe, raid)
* These functions are called by using the dfunc field in the mdimpp list.
* Each process function only understands its own type of metadevice. Once
* it processes the metadevice it was called for, it then loops through
* all of the underlying metadevices. After printing the name of the
* underlying metadevice, it puts in on a list to be processed. If the
* underlying device is a physical device, then print_physical_device is
* called.
* Once all information about the original metadevice is processed, it
* loops through the list of underlying metadevices and calls the
* appropriate function to process them.
*
* - process_toplevel_softparts()
* To match the output for metastat -c, all top-level softpartions
* are printed out in groups based on their underlying metadevice--so that
* the underlying metadevice only needs to be processed once.
*
* - meta_get_(sm_state, raid_col_state, stripe_state, hs_state)
* These functions are used to retrieve the metadevice state information.
* They are also used by the metastat concise routines in
* cmd/lvm/util/metastat.c.
*
*/
/*
* md_im_rec is a doubly linked list used to store the rb_data for each
* directory entry that corresponds to a metadevice.
* n_key: is set, if there is an associated entry in the NM namespace.
* dfunc: is set to point to the function that processes the particular
* metadevice associated with the record.
* hs_record_id: is only set, if the metadevice is a hotspare.
* un_self_id: is set for all other records. This is also used to generate
* the name of the metadevice if there is no entry for the metadevice in
* the NM namespace--n_key is not set.
*/
typedef struct md_im_rec {
void (*dfunc) ();
/* pointer to the unit structure for the metadevice, e.g. rb_data[0] */
void *record;
} md_im_rec_t;
/*
* md_im_list is used to group toplevel metadevices by type and to group
* the underlying devices for a particular metadevice.
*/
typedef struct md_im_list {
} md_im_list_t;
/*
* MAXSIZEMDRECNAME is the value that has historically been used to allocate
* space for the metadevice name
*/
#define NOT_PHYSICAL_DEV 0
/*
* strip_blacks()
*
* Strip blanks from string. Used for size field in concise output.
*/
static char *
strip_blanks(char *s)
{
char *p;
for (p = s; *p; ) {
if (*p == ' ') {
char *t;
for (t = p; *t; t++) {
*t = *(t + 1);
}
} else {
p++;
}
}
return (s);
}
/*
* print_concise_entry()
*
* Print properly indented metadevice name, type and size for concise output.
* This function is also called from: cmd/lvm/util/metastat.c.
*/
void
{
int i;
char *sz;
in[0] = 0;
for (i = 0; i < indent; i++)
/* set up minimum field width. negative for left justified */
if (width < 0)
width = 0; /* overflowed; no minimum field needed */
else
if (size == 0) {
sz = "-";
} else {
}
}
/*
* free_mdrec_list_entry()
*
* Removing entry from the list of metadevices in the diskset(mdimpp).
* This function will not remove the dummy entry at the head of the
* list, so we don't have to set mdrec equal to NULL.
*/
static void
{
}
}
/*
* ucomponent_append()
*
* Appending entry to the underlying component list. The list
* is used to group all of the underlying devices before
* processing them.
*/
static void
)
{
if (*ucomp_head == NULL) {
*ucomp_head = ucomp;
*ucomp_tail = ucomp;
} else {
}
}
/*
* free_md_im_list_entries()
*
* Freeing entries on an md_im_list_t. This list is used to group
* underlying components for processing and to group top-level metadevices
* by type.
*/
static void
{
while (tmp_list_entry != NULL) {
}
}
/*
* print_physical_device()
*
* If a metadevice has an underlying component that is a physical
* device, then this searches the pnm_rec_t list to match an entry's
* n_key to the key for the underlying component. The ctd name of the
* physical device is printed on the same line as the metadevice.
*/
static void
)
{
break;
}
}
}
/*
* get_stripe_req_size()
*
* Given a 64bit stripe unit, compute the size of the stripe unit.
* This function is a derivation of:
* common/lvm/md_convert.c:get_big_stripe_req_size()
* and any changes made to either this function or get_big_stripe_req_size()
* should be reviewed to make sure the functionality in both places is correct.
*
* Returns:
* total size of the 64bit stripe
*/
{
/* Compute the offset of the first component */
first_comp = sizeof (ms_unit_t) +
/*
* Requestor wants to have the total size, add the sizes of
* all components
*/
return (mdsize);
}
/*
* meta_get_sm_state()
*
* Gets the state for the underlying components(submirrors) of a mirror.
* This function is also called from: cmd/lvm/util/metastat.c.
*
* Returns:
* string for state of the sub-mirror
*/
static char *
)
{
/* all is well */
if (state & SMS_RUNNING) {
return (NULL);
}
/* resyncing, needs repair */
SMS_OFFLINE_RESYNC))) {
return (gettext("resyncing"));
}
/* needs repair */
return (gettext("maint"));
/* unknown */
return (gettext("unknown"));
}
/*
* meta_get_raid_col_state()
*
* Gets the state for the underlying components(columns) of a raid.
* This function is also called from: cmd/lvm/util/metastat.c.
*
* Returns:
* string for state of the raid column
*
*/
char *
)
{
switch (state) {
case RCS_INIT:
return (gettext("initializing"));
case RCS_OKAY:
return (NULL);
case RCS_INIT_ERRED:
/*FALLTHROUGH*/
case RCS_ERRED:
return (gettext("maint"));
case RCS_LAST_ERRED:
return (gettext("last-erred"));
case RCS_RESYNC:
return (gettext("resyncing"));
default:
return (gettext("unknown"));
}
}
/*
* meta_get_stripe_state()
*
* Gets the state for the underlying components of a stripe.
* This function is also called from: cmd/lvm/util/metastat.c.
*
* Returns:
* string for state of the stripe
*
*/
char *
)
{
switch (state) {
case CS_OKAY:
return (NULL);
case CS_ERRED:
return (gettext("maint"));
case CS_LAST_ERRED:
return (gettext("last-erred"));
case CS_RESYNC:
return (gettext("resyncing"));
default:
return (gettext("invalid"));
}
}
/*
* meta_get_hs_state()
*
* Gets the state for the underlying components(hotspares) of a hotspare pool.
* This function is also called from: cmd/lvm/util/metastat.c.
*
* Returns:
* string for state of the hotspare
*
*/
char *
)
{
switch (state) {
case HSS_AVAILABLE:
return (NULL);
case HSS_RESERVED:
return (gettext("in-use"));
case HSS_BROKEN:
return (gettext("broken"));
case HSS_UNUSED:
/* FALLTHROUGH */
default:
return (gettext("invalid"));
}
}
/*
* process_trans()
*
* Prints unit information for a trans metadevice and calls the respective
* functions to process the underlying metadevices.
*
*/
static void
int indent,
)
{
/* Printing name, size, and type of metadevice */
/*
* Loops through md_im_rec_t **mdimpp list of all metadevices to find
* record that matches the underlying device.
* Trans devices can only have one underlying device, so once a
* match is found, we are done.
*/
/* Printing name of the underlying metadevice */
break;
}
}
/*
* If a metadevice was not found, then the underlying device must be a
* physical device. Otherwise, call the functions to process the
* underlying devices.
*/
if (underlying_device == PHYSICAL_DEV) {
(void) printf("\n");
} else {
/* process underlying component */
(void) printf("\n");
indent += META_INDENT;
}
/*
* Removing the md_entry from the list
* of all metadevices
*/
}
/*
* process_hotspare()
*
* Searches though list of physical devices to match hotspare record.
* Prints physical device name and state of a hotspare unit.
*
*/
/*ARGSUSED*/
static void
int indent,
)
{
/*
* Loops through physical namespace to find the device that matches
* the hotspare entry.
*/
if (tmpphys_nm->n_key ==
/* Printing name of hotspare device */
break;
}
}
/* Not removing entry, because it can be processed more than once. */
}
/*
* process_hotspare_pool()
*
* Prints concise unit information for a hotspare pool metadevice and calls a
* function to process each attached hotspare device.
*
*/
static void
int indent,
)
{
int i;
/*
* Printing name, size, and type of metadevice. Setting size field to
* 0, so that output is the as metastat -c.
*/
0, 'h');
/* Looping through list of attached hotspare devices. */
for (i = 0; i < hsp->hsp_nhotspares; i++) {
/* Looking for the matching record for the hotspare device. */
/* Calling function to print name of hotspare */
tmpmdrec);
}
}
}
(void) printf("\n");
/*
* Removing the md_entry from the list
* of all metadevices
*/
}
/*
* process_raid()
*
* Prints concise unit information for a raid metadevice and calls the
* respective functions to process the underlying metadevices.
*
*/
static void
int indent,
)
{
int i;
int underlying_device;
/* Printing name, size, and type of metadevice */
/* Loops through raid columns to find underlying metadevices */
i++, mc++) {
/*
* Need to assume that underlying device is a physical device,
* unless we find a matching metadevice record.
*/
/*
* Loops through list of metadevices to find record that matches
* the underlying device.
*/
/* check if hotspare device enabled */
/*
* Find matching metadevice record
* for the hotspare device.
*/
for (hstmpmdrec = *mdimpp;
hstmpmdrec != NULL;
if (hstmpmdrec->hs_record_id ==
/* print name of hs */
break;
}
}
}
/* print name of underlying metadevice */
ucomp);
}
}
if (underlying_device == PHYSICAL_DEV) {
}
/*
* An underlying hotspare must be a physical device.
* If support is ever added for soft-partitions under
* hotspare pools, then this code should be updated to
* include a search for underlying metadevices.
*/
break;
}
}
}
hsname);
else
}
}
(void) printf("\n");
/* process underlying components */
indent += META_INDENT;
}
/*
* Removing the md_entry from the list
* of all metadevices
*/
}
/*
* process_mirror()
*
* Prints concise unit information for a mirror metadevice and calls the
* respective functions to process the underlying metadevices.
*
*/
static void
int indent,
)
{
int i;
/* Printing name, size, and type of metadevice */
/* Looping through sub-mirrors to find underlying devices */
ucomp);
}
}
/*
* It is not possible to have an underlying physical device
* for a submirror, so there is no need to search the phys_nm
* list.
*/
/* Printing the state for the submirror */
}
}
(void) printf("\n");
/* process underlying components */
indent += META_INDENT;
}
/*
* Removing the md_entry from the list
* of all metadevices
*/
}
/*
* process_stripe()
*
* Prints concise unit information for a stripe metadevice and calls the
* respective functions to process the underlying metadevices.
*
*/
static void
int indent,
)
{
int underlying_device;
/* Printing name, size, and type of metadevice */
/* Looping through stripe rows */
/*
* Looping through the components in each row to find the
* underlying devices.
*/
++comp, ++c) {
/*
* Need to assume that underlying device is a
* physical device, unless we find a matching
* metadevice record.
*/
&ucomp_tail, ucomp);
}
}
/* if an underlying metadevice was not found */
if (underlying_device == PHYSICAL_DEV) {
}
/*
* An underlying hotspare must be a physical device.
* If support is ever added for soft-partitions under
* hotspare pools, then this code should be updated to
* include a search for underlying metadevices.
*/
if (tmpphys_nm->n_key ==
break;
}
}
}
hsname);
} else {
}
}
}
}
(void) printf("\n");
/* Process underlying metadevices */
indent += META_INDENT;
}
/*
* Removing the md_entry from the list
* of all metadevices
*/
}
/*
* process_softpart()
*
* Prints concise unit information for a softpart metadevice and calls the
* respective functions to process the underlying metadevices.
*
*/
static void
int indent,
)
{
/* Printing name, size, and type of metadevice */
/*
* Loops through md_im_rec_t **mdimpp list of all metadevices to find
* record that matches the underlying device.
* Softpartitions can only have one underlying device, so once a
* match is found, we are done.
*/
/* Printing name of the underlying metadevice */
break;
}
}
/* This is only executed if an underlying metadevice was not found */
if (underlying_device == PHYSICAL_DEV) {
(void) printf("\n");
} else {
/* Process underlying metadevice */
(void) printf("\n");
indent += META_INDENT;
tmpmdrec);
}
/*
* Removing the md_entry from the list
* of all metadevices
*/
}
/*
* process_toplevel_softparts()
*
* Toplevel softpartions need to be grouped so that their underlying devices
* can be printed just once.
*/
static void
int indent,
)
{
int underlying_device;
/*
* Loops through md_im_rec_t **mdimpp list of all metadevices to find
* a parent). Groups output for these entries so that the function to
* process the underlying metadevice is only called once.
*/
(mdrec->has_parent == 0)) {
/* Printing name, size, and type of metadevice */
/*
* Looking for record that matches underlying
* component.
*/
/* Print name of underlying device */
(void) printf(" %s",
comp_mdrec->n_name);
break;
}
}
if (underlying_device == PHYSICAL_DEV) {
}
(void) printf("\n");
/*
* Looking for any other toplevel softpartitions with
* same underlying device. We know that all other
* matching metadevices, that share the same underlying
* metadevice, are also soft-partitions.
*/
if ((tmp_mdrec->has_parent == 0) &&
if (underlying_device ==
(void) printf(" %s",
comp_mdrec->n_name);
} else {
}
(void) printf("\n");
/*
* Need to advance so that will not lose
* position after removing processed
* record.
*/
/*
* Removing the md_entry from the list
* of all metadevices.
*/
} else {
}
}
/* Process the underlying device */
if (underlying_device == NOT_PHYSICAL_DEV) {
indent += META_INDENT;
}
}
}
}
/*
* process_toplevel_devices()
*
* Search through list of metadevices for metadevices of md_type that do not
* have a parent.
*
*/
static void
)
{
int indent = 0;
indent += META_INDENT;
/*
* Need to group soft partitions so that common underlying device
* are only processed once.
*/
if (md_type == MDDB_F_SOFTPART) {
return;
}
/*
* Search the list of metadevices to find all metadevices that match
* the type and don't have a parent. Put them on a separate list
* that will be processed.
*/
if (mdrec_tl_tail == NULL) {
} else {
}
}
}
/*
* Loop through list and process all top-level metadevices of a
* given type.
*/
tmp_tl_list->mdrec);
}
}
/*
* extract_mduser_data()
*
* Converts or copies the (mddb_rb_t->rb_data) metadevice record to a 64bit
* record.
* Sets the dfunc field to point to the appropriate function to process the
* metadevice.
* Sets the parent field for the metadevice.
* Extracts the name from the NM namespace if it is available, otherwise
* generates it from the metadevice's minor number.
*
* Returns:
* < 0 for failure
* 0 for success
*
*/
static int
void *rbp,
int is_32bit_record,
)
{
/*LINTED*/
/*
* Setting the un_self_id or the hs_self_id, in the case of hotspare
* records, for each metadevice entry. Also setting has_parent and
* setting dfunc so that it points to the correct function to process
* the record type.
* If the record was stored ondisk in 32bit format, then it is
* converted to the 64bits equivalent 64bit format and the memory
* for the 32bit pointer is freed.
*/
case MDDB_F_SOFTPART:
if (is_32bit_record) {
(sizeof (mddb_rb_t) - sizeof (int)));
newreqsize = sizeof (mp_unit_t) +
sizeof (struct mp_ext));
} else {
(sizeof (mddb_rb_t) - sizeof (int)));
newreqsize = sizeof (mp_unit_t) +
sizeof (struct mp_ext));
}
break;
case MDDB_F_STRIPE:
if (is_32bit_record) {
(sizeof (mddb_rb_t) - sizeof (int)));
} else {
(sizeof (mddb_rb_t) - sizeof (int)));
}
break;
case MDDB_F_MIRROR:
if (is_32bit_record) {
(sizeof (mddb_rb_t) - sizeof (int)));
newreqsize = sizeof (mm_unit_t);
} else {
(sizeof (mddb_rb_t) - sizeof (int)));
newreqsize = sizeof (mm_unit_t);
}
break;
case MDDB_F_RAID:
if (is_32bit_record) {
(sizeof (mddb_rb_t) - sizeof (int)));
newreqsize = sizeof (mr_unit_t) +
} else {
(sizeof (mddb_rb_t) - sizeof (int)));
newreqsize = sizeof (mr_unit_t) +
}
break;
case MDDB_F_TRANS_MASTER:
if (is_32bit_record) {
(sizeof (mddb_rb_t) - sizeof (int)));
newreqsize = sizeof (mt_unit_t);
} else {
(sizeof (mddb_rb_t) - sizeof (int)));
newreqsize = sizeof (mt_unit_t);
}
break;
case MDDB_F_HOTSPARE:
if (is_32bit_record) {
(sizeof (mddb_rb_t) - sizeof (int)));
newreqsize = sizeof (hot_spare_t);
} else {
(sizeof (mddb_rb_t) - sizeof (int)));
newreqsize = sizeof (hot_spare_t);
}
break;
case MDDB_F_HOTSPARE_POOL:
/*
* Ondisk and incore records are always same size.
*/
(sizeof (mddb_rb_t) - sizeof (int)));
newreqsize = sizeof (hot_spare_pool_ond_t) +
/*
* If the hsp has descriptive name we'll get
* the un_self_id
*/
else
mdrec->has_parent = 0;
break;
/* All valid cases have been dealt with */
default:
return (-1);
}
/*
* If metadevice record has an entry in the NM namespace
* then it is copied into the mdrec->n_name field.
*/
/*LINTED*/
/*
* Extract the metadevice/hsp name if it is
* in the namespace.
*
* If it is a hot spare pool we will find our
* match by comparing the NM record's n_key
* with the extracted key from the hsp_self_id
* Else, match the un_self_id for the record
* to the n_minor name in the NM record.
*/
break;
}
} else {
break;
}
}
}
}
/*
* If the metadevice name is not in the namespace, then
* then we will generate the name from the minor number
* for the metadevice. In the case of records for a hotspare
* pool we use hsp_self_id, otherwise we use un_self_id.
*/
/*
* Generate the metadevice name for all other records
* (except for hotspares, because hotspares can only
* be physical devices.)
*/
}
}
return (0);
}
/*
* read_mdrecord()
*
* Reads the mddb_rb32_od_t or mddb_rb_t and the associated metadevice record
* from the disk. Runs magic, checksum, and revision checks on the record
* block.
*
* Returns:
* < 0 for failure
* 0 for success
*
*/
static int
char *diskname,
int fd,
)
{
int is_32bit_record;
/* Read in the appropriate record and return configurations */
return (rval);
}
return (rval);
}
return (rval);
}
}
/*
* The only place to discover whether or not the record is a
* 32bit or 64bit record is from the record's rb_revision field.
* The mddb_rb_t and mddb_rb32_t structures are identical for the
* following fields:
* rb_magic, rb_revision, rb_checksum, and rb_checksum_fiddle.
* So we can assume that the record is a 32bit structure when we
* check the record's magic number and revision and when we calculate
* the records checksum.
*/
/*
* Checking the magic number for the record block.
*/
rval = -1;
goto out;
}
/*
* Checking the revision for the record block. Must match either
* revision for the current 64bit or 32bit record block. Also,
* setting the flag for whether or not it is a 32bit record.
*/
is_32bit_record = 0;
switch (rbp_32->rb_revision) {
case MDDB_REV_RB:
case MDDB_REV_RBFN:
is_32bit_record = 1;
break;
case MDDB_REV_RB64:
case MDDB_REV_RB64FN:
break;
default:
rval = -1;
goto out;
}
/*
* Calculating the checksum for this record block. Need
* to skip the rb's checksum fiddle.
*/
rval = -1;
goto out;
}
/* mddb_rb_t and mddb_rb32_t differ before the rb_timestamp field */
if (!is_32bit_record) {
} else if ((*lastaccess).tv_sec ==
if ((*lastaccess).tv_usec <
}
} else {
} else if ((*lastaccess).tv_sec ==
if ((*lastaccess).tv_usec <
}
}
/* Populates the fields in md_im_rec_t *tmp_mdrec. */
if (rval < 0)
goto out;
/* Adding record to the head of the list of all metadevices. */
} else {
}
out:
/* Free the skip list */
while (skip) {
}
if (rbp)
return (rval);
}
/*
* read_all_mdrecords()
*
* Reads the directory block and directory entries.
* Runs magic, checksum, and revision checks on the directory block.
*
* Returns:
* < 0 for failure
* 0 for success
*/
static int
int fd,
)
{
int desize;
/*LINTED*/
/* Read in all directory blocks */
dbblk != 0;
goto out;
/*
* Set ep with error code for MDE_DB_NODB. This is the
* error code used in the kernel when there is a problem
* with reading records in. Checks the magic number, the
* revision, and the checksum for each directory block.
*/
rval = -1;
goto out;
}
rval = -1;
goto out;
}
rval = -1;
goto out;
}
/*
* If db timestamp is more recent than the previously recorded
* last modified timestamp, then update last modified.
*/
}
/* Creates dep list of all directory entries in the db */
/* LINTED */
+ sizeof (dbp->db_firstentry));
/* LINTED */
}
}
/*
* Process all directory entries in the directory block.
* For each directory entry, read_mdrec is called to read
* in the record data.
*/
/*
* de_flags is set to the type of metadevice.
* If directory entry does not correspond to a
* specific metadevice then it is set to zero.
* All namespace records(NM, SHR_NM, DID_SHR_NM) have a
* value of zero in their de_flags field.
*/
if (rval < 0)
goto out;
}
}
}
out:
return (rval);
}
/*
* report_metastat_info()
*
* Generates the metastat -c output. Also, updates the global variable
* for a last accessed timestamp.
*
* Returns:
* < 0 for failure
* 0 for success
*
*/
int
int fd,
)
{
int rval = 0;
/* list of all metadevices in diskset */
/* Read in metadevice records and add entries to mdimp list. */
ep);
if (rval < 0)
goto out;
/* Adding a fake record to the head of the list of all metadevices. */
}
/* Calling functions to process all metadevices on mdimp list */
(void) printf("\n");
out:
/*
* If mdreclist is not null, then this will walk through all
* elements and free them.
*/
}
return (rval);
}