2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A
2N/A/*
2N/A * Copyright (c) 2001, 2011, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A/*
2N/A * Just in case we're not in a build environment, make sure that
2N/A * TEXT_DOMAIN gets set to something.
2N/A */
2N/A#if !defined(TEXT_DOMAIN)
2N/A#define TEXT_DOMAIN "SYS_TEST"
2N/A#endif
2N/A
2N/A/*
2N/A * soft partition operations
2N/A *
2N/A * Soft Partitions provide a virtual disk mechanism which is used to
2N/A * divide a large volume into many small pieces, each appearing as a
2N/A * separate device. A soft partition consists of a series of extents,
2N/A * each having an offset and a length. The extents are logically
2N/A * contiguous, so where the first extent leaves off the second extent
2N/A * picks up. Which extent a given "virtual offset" belongs to is
2N/A * dependent on the size of all the previous extents in the soft
2N/A * partition.
2N/A *
2N/A * Soft partitions are represented in memory by an extent node
2N/A * (sp_ext_node_t) which contains all of the information necessary to
2N/A * create a unit structure and update the on-disk format, called
2N/A * "watermarks". These extent nodes are typically kept in a doubly
2N/A * linked list and are manipulated by list manipulation routines. A
2N/A * list of extents may represent all of the soft partitions on a volume,
2N/A * a single soft partition, or perhaps just a set of extents that need
2N/A * to be updated. Extent lists may be sorted by extent or by name/seq#,
2N/A * depending on which compare function is used. Most of the routines
2N/A * require the list be sorted by offset to work, and that's the typical
2N/A * configuration.
2N/A *
2N/A * In order to do an allocation, knowledge of all soft partitions on the
2N/A * volume is required. Then free space is determined from the space
2N/A * that is not allocated, and new allocations can be made from the free
2N/A * space. Once the new allocations are made, a unit structure is created
2N/A * and the watermarks are updated. The status is then changed to "okay"
2N/A * on the unit structure to commit the transaction. If updating the
2N/A * watermarks fails, the unit structure is in an intermediate state and
2N/A * the driver will not allow access to the device.
2N/A *
2N/A * A typical sequence of events is:
2N/A * 1. Fetch the list of names for all soft partitions on a volume
2N/A * meta_sp_get_by_component()
2N/A * 2. Construct an extent list from the name list
2N/A * meta_sp_extlist_from_namelist()
2N/A * 3. Fill the gaps in the extent list with free extents
2N/A * meta_sp_list_freefill()
2N/A * 4. Allocate from the free extents
2N/A * meta_sp_alloc_by_len()
2N/A * meta_sp_alloc_by_list()
2N/A * 5. Create the unit structure from the extent list
2N/A * meta_sp_createunit()
2N/A * meta_sp_updateunit()
2N/A * 6. Write out the watermarks
2N/A * meta_sp_update_wm()
2N/A * 7. Set the status to "Okay"
2N/A * meta_sp_setstatus()
2N/A *
2N/A */
2N/A
2N/A#include <stdio.h>
2N/A#include <meta.h>
2N/A#include "meta_repartition.h"
2N/A#include <sys/lvm/md_sp.h>
2N/A#include <sys/lvm/md_crc.h>
2N/A#include <strings.h>
2N/A#include <sys/lvm/md_mirror.h>
2N/A#include <sys/bitmap.h>
2N/A
2N/Aextern int md_in_daemon;
2N/A
2N/Atypedef struct sp_ext_node {
2N/A struct sp_ext_node *ext_next; /* next element */
2N/A struct sp_ext_node *ext_prev; /* previous element */
2N/A sp_ext_type_t ext_type; /* type of extent */
2N/A sp_ext_offset_t ext_offset; /* starting offset */
2N/A sp_ext_length_t ext_length; /* length of this node */
2N/A uint_t ext_flags; /* extent flags */
2N/A uint32_t ext_seq; /* watermark seq no */
2N/A mdname_t *ext_namep; /* name pointer */
2N/A mdsetname_t *ext_setp; /* set pointer */
2N/A} sp_ext_node_t;
2N/A
2N/A/* extent flags */
2N/A#define EXTFLG_UPDATE (1)
2N/A
2N/A/* Extent node compare function for list sorting */
2N/Atypedef int (*ext_cmpfunc_t)(sp_ext_node_t *, sp_ext_node_t *);
2N/A
2N/A
2N/A/* Function Prototypes */
2N/A
2N/A/* Debugging Functions */
2N/Astatic void meta_sp_debug(char *format, ...);
2N/Astatic void meta_sp_printunit(mp_unit_t *mp);
2N/A
2N/A/* Misc Support Functions */
2N/Aint meta_sp_parsesize(char *s, sp_ext_length_t *szp);
2N/Astatic int meta_sp_parsesizestring(char *s, sp_ext_length_t *szp);
2N/Astatic int meta_sp_setgeom(mdname_t *np, mdname_t *compnp, mp_unit_t *mp,
2N/A md_error_t *ep);
2N/Aint meta_sp_get_by_component(mdsetname_t *sp, mdname_t *compnp,
2N/A mdnamelist_t **nlpp, int force, md_error_t *ep);
2N/Astatic sp_ext_length_t meta_sp_get_default_alignment(mdsetname_t *sp,
2N/A mdname_t *compnp, md_error_t *ep);
2N/A
2N/A/* Extent List Manipulation Functions */
2N/Astatic int meta_sp_cmp_by_nameseq(sp_ext_node_t *e1, sp_ext_node_t *e2);
2N/Astatic int meta_sp_cmp_by_offset(sp_ext_node_t *e1, sp_ext_node_t *e2);
2N/Astatic void meta_sp_list_insert(mdsetname_t *sp, mdname_t *np,
2N/A sp_ext_node_t **head, sp_ext_offset_t offset, sp_ext_length_t length,
2N/A sp_ext_type_t type, uint_t seq, uint_t flags, ext_cmpfunc_t compare);
2N/Astatic void meta_sp_list_free(sp_ext_node_t **head);
2N/Astatic void meta_sp_list_remove(sp_ext_node_t **head, sp_ext_node_t *ext);
2N/Astatic sp_ext_length_t meta_sp_list_size(sp_ext_node_t *head,
2N/A sp_ext_type_t exttype, int exclude_wm);
2N/Astatic sp_ext_node_t *meta_sp_list_find(sp_ext_node_t *head,
2N/A sp_ext_offset_t offset);
2N/Astatic void meta_sp_list_freefill(sp_ext_node_t **extlist,
2N/A sp_ext_length_t size);
2N/Astatic void meta_sp_list_dump(sp_ext_node_t *head);
2N/Astatic int meta_sp_list_overlaps(sp_ext_node_t *head);
2N/A
2N/A/* Extent List Query Functions */
2N/Astatic boolean_t meta_sp_enough_space(int desired_number_of_sps,
2N/A blkcnt_t desired_sp_size, sp_ext_node_t **extent_listpp,
2N/A sp_ext_length_t alignment);
2N/Astatic boolean_t meta_sp_get_extent_list(mdsetname_t *mdsetnamep,
2N/A mdname_t *device_mdnamep, sp_ext_node_t **extent_listpp,
2N/A md_error_t *ep);
2N/Astatic boolean_t meta_sp_get_extent_list_for_drive(mdsetname_t *mdsetnamep,
2N/A mddrivename_t *mddrivenamep, sp_ext_node_t **extent_listpp);
2N/A
2N/A
2N/A/* Extent Allocation Functions */
2N/Astatic void meta_sp_alloc_by_ext(mdsetname_t *sp, mdname_t *np,
2N/A sp_ext_node_t **extlist, sp_ext_node_t *free_ext,
2N/A sp_ext_offset_t alloc_offset, sp_ext_length_t alloc_length, uint_t seq);
2N/Astatic int meta_sp_alloc_by_len(mdsetname_t *sp, mdname_t *np,
2N/A sp_ext_node_t **extlist, sp_ext_length_t *lp,
2N/A sp_ext_offset_t last_off, sp_ext_length_t alignment);
2N/Astatic int meta_sp_alloc_by_list(mdsetname_t *sp, mdname_t *np,
2N/A sp_ext_node_t **extlist, sp_ext_node_t *oblist);
2N/A
2N/A/* Extent List Population Functions */
2N/Astatic int meta_sp_extlist_from_namelist(mdsetname_t *sp, mdnamelist_t *spnlp,
2N/A sp_ext_node_t **extlist, md_error_t *ep);
2N/Astatic int meta_sp_extlist_from_wm(mdsetname_t *sp, mdname_t *compnp,
2N/A sp_ext_node_t **extlist, ext_cmpfunc_t compare, md_error_t *ep);
2N/A
2N/A/* Print (metastat) Functions */
2N/Astatic int meta_sp_short_print(md_sp_t *msp, char *fname, FILE *fp,
2N/A mdprtopts_t options, md_error_t *ep);
2N/Astatic char *meta_sp_status_to_name(xsp_status_t xsp_status, uint_t tstate);
2N/Astatic int meta_sp_report(mdsetname_t *sp, md_sp_t *msp, mdnamelist_t **nlpp,
2N/A char *fname, FILE *fp, mdprtopts_t options, md_error_t *ep);
2N/A
2N/A/* Watermark Manipulation Functions */
2N/Astatic int meta_sp_update_wm(mdsetname_t *sp, md_sp_t *msp,
2N/A sp_ext_node_t *extlist, md_error_t *ep);
2N/Astatic int meta_sp_clear_wm(mdsetname_t *sp, md_sp_t *msp, md_error_t *ep);
2N/Astatic int meta_sp_read_wm(mdsetname_t *sp, mdname_t *compnp,
2N/A mp_watermark_t *wm, sp_ext_offset_t offset, md_error_t *ep);
2N/Astatic diskaddr_t meta_sp_get_start(mdsetname_t *sp, mdname_t *compnp,
2N/A md_error_t *ep);
2N/A
2N/A/* Unit Structure Manipulation Functions */
2N/Astatic void meta_sp_fillextarray(mp_unit_t *mp, sp_ext_node_t *extlist);
2N/Astatic mp_unit_t *meta_sp_createunit(mdname_t *np, mdname_t *compnp,
2N/A sp_ext_node_t *extlist, int numexts, sp_ext_length_t len,
2N/A sp_status_t status, md_error_t *ep);
2N/Astatic mp_unit_t *meta_sp_updateunit(mdname_t *np, mp_unit_t *old_un,
2N/A sp_ext_node_t *extlist, sp_ext_length_t grow_len, int numexts,
2N/A md_error_t *ep);
2N/Astatic int meta_create_sp(mdsetname_t *sp, md_sp_t *msp, sp_ext_node_t *oblist,
2N/A mdcmdopts_t options, sp_ext_length_t alignment, md_error_t *ep);
2N/Astatic int meta_check_sp(mdsetname_t *sp, md_sp_t *msp, mdcmdopts_t options,
2N/A int *repart_options, md_error_t *ep);
2N/A
2N/A/* Reset (metaclear) Functions */
2N/Astatic int meta_sp_reset_common(mdsetname_t *sp, mdname_t *np, md_sp_t *msp,
2N/A md_sp_reset_t reset_params, mdcmdopts_t options, md_error_t *ep);
2N/A
2N/A/* Recovery (metarecover) Functions */
2N/Astatic void meta_sp_display_exthdr(void);
2N/Astatic void meta_sp_display_ext(sp_ext_node_t *ext);
2N/Astatic int meta_sp_checkseq(sp_ext_node_t *extlist);
2N/Astatic int meta_sp_resolve_name_conflict(mdsetname_t *, mdname_t *,
2N/A mdname_t **, md_error_t *);
2N/Astatic int meta_sp_validate_wm(mdsetname_t *sp, mdname_t *np,
2N/A mdcmdopts_t options, md_error_t *ep);
2N/Astatic int meta_sp_validate_unit(mdsetname_t *sp, mdname_t *compnp,
2N/A mdcmdopts_t options, md_error_t *ep);
2N/Astatic int meta_sp_validate_wm_and_unit(mdsetname_t *sp, mdname_t *np,
2N/A mdcmdopts_t options, md_error_t *ep);
2N/Astatic int meta_sp_validate_exts(mdname_t *np, sp_ext_node_t *wmext,
2N/A sp_ext_node_t *unitext, md_error_t *ep);
2N/Astatic int meta_sp_recover_from_wm(mdsetname_t *sp, mdname_t *compnp,
2N/A mdcmdopts_t options, md_error_t *ep);
2N/Astatic int meta_sp_recover_from_unit(mdsetname_t *sp, mdname_t *np,
2N/A mdcmdopts_t options, md_error_t *ep);
2N/A
2N/A/*
2N/A * Private Constants
2N/A */
2N/A
2N/Astatic const int FORCE_RELOAD_CACHE = 1;
2N/Astatic const uint_t NO_FLAGS = 0;
2N/Astatic const sp_ext_offset_t NO_OFFSET = 0ULL;
2N/Astatic const uint_t NO_SEQUENCE_NUMBER = 0;
2N/Astatic const int ONE_SOFT_PARTITION = 1;
2N/A
2N/Astatic unsigned long *sp_parent_printed[MD_MAXSETS];
2N/A
2N/A#define TEST_SOFT_PARTITION_NAMEP NULL
2N/A#define TEST_SETNAMEP NULL
2N/A
2N/A#define EXCLUDE_WM (1)
2N/A#define INCLUDE_WM (0)
2N/A
2N/A#define SP_UNALIGNED (0LL)
2N/A
2N/A/*
2N/A * **************************************************************************
2N/A * Debugging Functions *
2N/A * **************************************************************************
2N/A */
2N/A
2N/A/*PRINTFLIKE1*/
2N/Astatic void
2N/Ameta_sp_debug(char *format, ...)
2N/A{
2N/A static int debug;
2N/A static int debug_set = 0;
2N/A va_list ap;
2N/A
2N/A if (!debug_set) {
2N/A debug = getenv(META_SP_DEBUG) ? 1 : 0;
2N/A debug_set = 1;
2N/A }
2N/A
2N/A if (debug) {
2N/A va_start(ap, format);
2N/A (void) vfprintf(stderr, format, ap);
2N/A va_end(ap);
2N/A }
2N/A}
2N/A
2N/Astatic void
2N/Ameta_sp_printunit(mp_unit_t *mp)
2N/A{
2N/A int i;
2N/A
2N/A if (mp == NULL)
2N/A return;
2N/A
2N/A /* print the common fields we know about */
2N/A (void) fprintf(stderr, "\tmp->c.un_type: %d\n", mp->c.un_type);
2N/A (void) fprintf(stderr, "\tmp->c.un_size: %u\n", mp->c.un_size);
2N/A (void) fprintf(stderr, "\tmp->c.un_self_id: %lu\n", MD_SID(mp));
2N/A
2N/A /* sp-specific fields */
2N/A (void) fprintf(stderr, "\tmp->un_status: %u\n", mp->un_status);
2N/A (void) fprintf(stderr, "\tmp->un_numexts: %u\n", mp->un_numexts);
2N/A (void) fprintf(stderr, "\tmp->un_length: %llu\n", mp->un_length);
2N/A (void) fprintf(stderr, "\tmp->un_dev(32): 0x%llx\n", mp->un_dev);
2N/A (void) fprintf(stderr, "\tmp->un_dev(64): 0x%llx\n", mp->un_dev);
2N/A (void) fprintf(stderr, "\tmp->un_key: %d\n", mp->un_key);
2N/A
2N/A /* print extent information */
2N/A (void) fprintf(stderr, "\tExt#\tvoff\t\tpoff\t\tLen\n");
2N/A for (i = 0; i < mp->un_numexts; i++) {
2N/A (void) fprintf(stderr, "\t%d\t%llu\t\t%llu\t\t%llu\n", i,
2N/A mp->un_ext[i].un_voff, mp->un_ext[i].un_poff,
2N/A mp->un_ext[i].un_len);
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * FUNCTION: meta_sp_parsesize()
2N/A * INPUT: s - the string to parse
2N/A * OUTPUT: *szp - disk block count (0 for "all")
2N/A * RETURNS: -1 for error, 0 for success
2N/A * PURPOSE: parses the command line parameter that specifies the
2N/A * requested size of a soft partition. The input string
2N/A * is either the literal "all" or a numeric value
2N/A * followed by a single character, b for disk blocks, k
2N/A * for kilobytes, m for megabytes, g for gigabytes, or t
2N/A * for terabytes. p for petabytes and e for exabytes
2N/A * have been added as undocumented features for future
2N/A * expansion. For example, 100m is 100 megabytes, while
2N/A * 50g is 50 gigabytes. All values are rounded up to the
2N/A * nearest block size.
2N/A */
2N/Aint
2N/Ameta_sp_parsesize(char *s, sp_ext_length_t *szp)
2N/A{
2N/A if (s == NULL || szp == NULL) {
2N/A return (-1);
2N/A }
2N/A
2N/A /* Check for literal "all" */
2N/A if (strcasecmp(s, "all") == 0) {
2N/A *szp = 0;
2N/A return (0);
2N/A }
2N/A
2N/A return (meta_sp_parsesizestring(s, szp));
2N/A}
2N/A
2N/A/*
2N/A * FUNCTION: meta_sp_parsesizestring()
2N/A * INPUT: s - the string to parse
2N/A * OUTPUT: *szp - disk block count
2N/A * RETURNS: -1 for error, 0 for success
2N/A * PURPOSE: parses a string that specifies size. The input string is a
2N/A * numeric value followed by a single character, b for disk blocks,
2N/A * k for kilobytes, m for megabytes, g for gigabytes, or t for
2N/A * terabytes. p for petabytes and e for exabytes have been added
2N/A * as undocumented features for future expansion. For example,
2N/A * 100m is 100 megabytes, while 50g is 50 gigabytes. All values
2N/A * are rounded up to the nearest block size.
2N/A */
2N/Astatic int
2N/Ameta_sp_parsesizestring(char *s, sp_ext_length_t *szp)
2N/A{
2N/A sp_ext_length_t len = 0;
2N/A char len_type[2];
2N/A
2N/A if (s == NULL || szp == NULL) {
2N/A return (-1);
2N/A }
2N/A
2N/A /*
2N/A * make sure block offset does not overflow 2^64 bytes.
2N/A */
2N/A if ((sscanf(s, "%llu%1[BbKkMmGgTt]", &len, len_type) != 2) ||
2N/A (len == 0LL) ||
2N/A (len > (1LL << (64 - DEV_BSHIFT))))
2N/A return (-1);
2N/A
2N/A switch (len_type[0]) {
2N/A case 'B':
2N/A case 'b':
2N/A len = lbtodb(roundup(len * DEV_BSIZE, DEV_BSIZE));
2N/A break;
2N/A case 'K':
2N/A case 'k':
2N/A len = lbtodb(roundup(len * 1024ULL, DEV_BSIZE));
2N/A break;
2N/A case 'M':
2N/A case 'm':
2N/A len = lbtodb(roundup(len * 1024ULL*1024ULL, DEV_BSIZE));
2N/A break;
2N/A case 'g':
2N/A case 'G':
2N/A len = lbtodb(roundup(len * 1024ULL*1024ULL*1024ULL, DEV_BSIZE));
2N/A break;
2N/A case 't':
2N/A case 'T':
2N/A len = lbtodb(roundup(len * 1024ULL*1024ULL*1024ULL*1024ULL,
2N/A DEV_BSIZE));
2N/A break;
2N/A case 'p':
2N/A case 'P':
2N/A len = lbtodb(roundup(
2N/A len * 1024ULL*1024ULL*1024ULL*1024ULL*1024ULL,
2N/A DEV_BSIZE));
2N/A break;
2N/A case 'e':
2N/A case 'E':
2N/A len = lbtodb(roundup(
2N/A len * 1024ULL*1024ULL*1024ULL*1024ULL*1024ULL*1024ULL,
2N/A DEV_BSIZE));
2N/A break;
2N/A default:
2N/A /* error */
2N/A return (-1);
2N/A }
2N/A
2N/A *szp = len;
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * FUNCTION: meta_sp_setgeom()
2N/A * INPUT: np - the underlying device to setup geometry for
2N/A * compnp - the underlying device to setup geometry for
2N/A * mp - the unit structure to set the geometry for
2N/A * OUTPUT: ep - return error pointer
2N/A * RETURNS: int - -1 if error, 0 otherwise
2N/A * PURPOSE: establishes geometry information for a device
2N/A */
2N/Astatic int
2N/Ameta_sp_setgeom(
2N/A mdname_t *np,
2N/A mdname_t *compnp,
2N/A mp_unit_t *mp,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A mdgeom_t *geomp;
2N/A uint_t round_cyl = 0;
2N/A
2N/A if ((geomp = metagetgeom(compnp, ep)) == NULL)
2N/A return (-1);
2N/A if (meta_setup_geom((md_unit_t *)mp, np, geomp, geomp->write_reinstruct,
2N/A geomp->read_reinstruct, round_cyl, ep) != 0)
2N/A return (-1);
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * FUNCTION: meta_sp_setstatus()
2N/A * INPUT: sp - the set name for the devices to set the status on
2N/A * minors - an array of minor numbers of devices to set status on
2N/A * num_units - number of entries in the array
2N/A * status - status value to set all units to
2N/A * OUTPUT: ep - return error pointer
2N/A * RETURNS: int - -1 if error, 0 success
2N/A * PURPOSE: sets the status of one or more soft partitions to the
2N/A * requested value
2N/A */
2N/Aint
2N/Ameta_sp_setstatus(
2N/A mdsetname_t *sp,
2N/A minor_t *minors,
2N/A int num_units,
2N/A sp_status_t status,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A md_sp_statusset_t status_params;
2N/A
2N/A assert(minors != NULL);
2N/A
2N/A /* update status of all soft partitions to the status passed in */
2N/A (void) memset(&status_params, 0, sizeof (status_params));
2N/A status_params.num_units = num_units;
2N/A status_params.new_status = status;
2N/A status_params.size = num_units * sizeof (minor_t);
2N/A status_params.minors = (uintptr_t)minors;
2N/A MD_SETDRIVERNAME(&status_params, MD_SP, sp->setno);
2N/A if (metaioctl(MD_IOC_SPSTATUS, &status_params, &status_params.mde,
2N/A NULL) != 0) {
2N/A (void) mdstealerror(ep, &status_params.mde);
2N/A return (-1);
2N/A }
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * FUNCTION: meta_get_sp_names()
2N/A * INPUT: sp - the set name to get soft partitions from
2N/A * options - options from the command line
2N/A * OUTPUT: nlpp - list of all soft partition names
2N/A * ep - return error pointer
2N/A * RETURNS: int - -1 if error, 0 success
2N/A * PURPOSE: returns a list of all soft partitions in the metadb
2N/A * for all devices in the specified set
2N/A */
2N/Aint
2N/Ameta_get_sp_names(
2N/A mdsetname_t *sp,
2N/A mdnamelist_t **nlpp,
2N/A int options,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A return (meta_get_names(MD_SP, sp, nlpp, options, ep));
2N/A}
2N/A
2N/A/*
2N/A * FUNCTION: meta_get_by_component()
2N/A * INPUT: sp - the set name to get soft partitions from
2N/A * compnp - the name of the device containing the soft
2N/A * partitions that will be returned
2N/A * force - 0 - reads cached namelist if available,
2N/A * 1 - reloads cached namelist, frees old namelist
2N/A * OUTPUT: nlpp - list of all soft partition names
2N/A * ep - return error pointer
2N/A * RETURNS: int - -1 error, otherwise the number of soft partitions
2N/A * found on the component (0 = none found).
2N/A * PURPOSE: returns a list of all soft partitions on a given device
2N/A * from the metadb information
2N/A */
2N/Aint
2N/Ameta_sp_get_by_component(
2N/A mdsetname_t *sp,
2N/A mdname_t *compnp,
2N/A mdnamelist_t **nlpp,
2N/A int force,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A static mdnamelist_t *cached_list = NULL; /* cached namelist */
2N/A static int cached_count = 0; /* cached count */
2N/A mdnamelist_t *spnlp = NULL; /* all sp names */
2N/A mdnamelist_t *namep; /* list iterator */
2N/A mdnamelist_t **tailpp = nlpp; /* namelist tail */
2N/A mdnamelist_t **cachetailpp; /* cache tail */
2N/A md_sp_t *msp; /* unit structure */
2N/A int count = 0; /* count of sp's */
2N/A int err;
2N/A mdname_t *curnp;
2N/A
2N/A if ((cached_list != NULL) && (!force)) {
2N/A /* return a copy of the cached list */
2N/A for (namep = cached_list; namep != NULL; namep = namep->next)
2N/A tailpp = meta_namelist_append_wrapper(tailpp,
2N/A namep->namep);
2N/A return (cached_count);
2N/A }
2N/A
2N/A /* free the cache and reset values to zeros to prepare for a new list */
2N/A metafreenamelist(cached_list);
2N/A cached_count = 0;
2N/A cached_list = NULL;
2N/A cachetailpp = &cached_list;
2N/A *nlpp = NULL;
2N/A
2N/A /* get all the softpartitions first of all */
2N/A if (meta_get_sp_names(sp, &spnlp, 0, ep) < 0)
2N/A return (-1);
2N/A
2N/A /*
2N/A * Now for each sp, see if it resides on the component we
2N/A * are interested in, if so then add it to our list
2N/A */
2N/A for (namep = spnlp; namep != NULL; namep = namep->next) {
2N/A curnp = namep->namep;
2N/A
2N/A /* get the unit structure */
2N/A if ((msp = meta_get_sp_common(sp, curnp, 0, ep)) == NULL)
2N/A continue;
2N/A
2N/A /*
2N/A * If the current soft partition is not on the same
2N/A * component, continue the search. If it is on the same
2N/A * component, add it to our namelist.
2N/A */
2N/A err = meta_check_samedrive(compnp, msp->compnamep, ep);
2N/A if (err <= 0) {
2N/A /* not on the same device, check the next one */
2N/A continue;
2N/A }
2N/A
2N/A /* it's on the same drive */
2N/A
2N/A /*
2N/A * Check for overlapping partitions if the component is not
2N/A * a metadevice.
2N/A */
2N/A if (!metaismeta(msp->compnamep)) {
2N/A /*
2N/A * if they're on the same drive, neither
2N/A * should be a metadevice if one isn't
2N/A */
2N/A assert(!metaismeta(compnp));
2N/A
2N/A if (meta_check_overlap(msp->compnamep->cname,
2N/A compnp, 0, -1, msp->compnamep, 0, -1, ep) == 0)
2N/A continue;
2N/A
2N/A /* in this case it's not an error for them to overlap */
2N/A mdclrerror(ep);
2N/A }
2N/A
2N/A /* Component is on the same device, add to the used list */
2N/A tailpp = meta_namelist_append_wrapper(tailpp, curnp);
2N/A cachetailpp = meta_namelist_append_wrapper(cachetailpp,
2N/A curnp);
2N/A
2N/A ++count;
2N/A ++cached_count;
2N/A }
2N/A
2N/A assert(count == cached_count);
2N/A return (count);
2N/A
2N/Aout:
2N/A metafreenamelist(*nlpp);
2N/A *nlpp = NULL;
2N/A return (-1);
2N/A}
2N/A
2N/A/*
2N/A * FUNCTION: meta_sp_get_default_alignment()
2N/A * INPUT: sp - the pertinent set name
2N/A * compnp - the name of the underlying component
2N/A * OUTPUT: ep - return error pointer
2N/A * RETURNS: sp_ext_length_t =0: no default alignment
2N/A * >0: default alignment
2N/A * PURPOSE: returns the default alignment for soft partitions to
2N/A * be built on top of the specified component or
2N/A * metadevice
2N/A */
2N/Astatic sp_ext_length_t
2N/Ameta_sp_get_default_alignment(
2N/A mdsetname_t *sp,
2N/A mdname_t *compnp,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A sp_ext_length_t a = SP_UNALIGNED;
2N/A char *mname;
2N/A
2N/A assert(compnp != NULL);
2N/A
2N/A /*
2N/A * We treat raw devices as opaque, and assume nothing about
2N/A * their alignment requirements.
2N/A */
2N/A if (!metaismeta(compnp))
2N/A return (SP_UNALIGNED);
2N/A
2N/A /*
2N/A * We already know it's a metadevice from the previous test;
2N/A * metagetmiscname() will tell us which metadevice type we
2N/A * have
2N/A */
2N/A mname = metagetmiscname(compnp, ep);
2N/A if (mname == NULL)
2N/A goto out;
2N/A
2N/A /*
2N/A * For a mirror, we want to deal with the stripe that is the
2N/A * primary side. If it happens to be asymmetrically
2N/A * configured, there is no simple way to fake a universal
2N/A * alignment. There's a chance that the least common
2N/A * denominator of the set of interlaces from all stripes of
2N/A * all submirrors would do it, but nobody that really cared
2N/A * that much about this issue would create an asymmetric
2N/A * config to start with.
2N/A *
2N/A * If the component underlying the soft partition is a mirror,
2N/A * then at the exit of this loop, compnp will have been
2N/A * updated to describe the first active submirror.
2N/A */
2N/A if (strcmp(mname, MD_MIRROR) == 0) {
2N/A md_mirror_t *mp;
2N/A int smi;
2N/A md_submirror_t *smp;
2N/A
2N/A mp = meta_get_mirror(sp, compnp, ep);
2N/A if (mp == NULL)
2N/A goto out;
2N/A
2N/A for (smi = 0; smi < NMIRROR; smi++) {
2N/A
2N/A smp = &mp->submirrors[smi];
2N/A if (smp->state == SMS_UNUSED)
2N/A continue;
2N/A
2N/A compnp = smp->submirnamep;
2N/A assert(compnp != NULL);
2N/A
2N/A mname = metagetmiscname(compnp, ep);
2N/A if (mname == NULL)
2N/A goto out;
2N/A
2N/A break;
2N/A }
2N/A
2N/A if (smi == NMIRROR)
2N/A goto out;
2N/A }
2N/A
2N/A /*
2N/A * Handle stripes and submirrors identically; just return the
2N/A * interlace of the first row.
2N/A */
2N/A if (strcmp(mname, MD_STRIPE) == 0) {
2N/A md_stripe_t *stp;
2N/A
2N/A stp = meta_get_stripe(sp, compnp, ep);
2N/A if (stp == NULL)
2N/A goto out;
2N/A
2N/A a = stp->rows.rows_val[0].interlace;
2N/A goto out;
2N/A }
2N/A
2N/A /*
2N/A * Raid is even more straightforward; the interlace applies to
2N/A * the entire device.
2N/A */
2N/A if (strcmp(mname, MD_RAID) == 0) {
2N/A md_raid_t *rp;
2N/A
2N/A rp = meta_get_raid(sp, compnp, ep);
2N/A if (rp == NULL)
2N/A goto out;
2N/A
2N/A a = rp->interlace;
2N/A goto out;
2N/A }
2N/A
2N/A /*
2N/A * If we have arrived here with the alignment still not set,
2N/A * then we expect the error to have been set by one of the
2N/A * routines we called. If neither is the case, something has
2N/A * really gone wrong above. (Probably the submirror walk
2N/A * failed to produce a valid submirror, but that would be
2N/A * really bad...)
2N/A */
2N/Aout:
2N/A meta_sp_debug("meta_sp_get_default_alignment: miscname %s, "
2N/A "alignment %lld\n", (mname == NULL) ? "NULL" : mname, a);
2N/A
2N/A if (getenv(META_SP_DEBUG) && !mdisok(ep)) {
2N/A mde_perror(ep, NULL);
2N/A }
2N/A
2N/A assert((a > 0) || (!mdisok(ep)));
2N/A
2N/A return (a);
2N/A}
2N/A
2N/A
2N/A
2N/A/*
2N/A * FUNCTION: meta_check_insp()
2N/A * INPUT: sp - the set name for the device to check
2N/A * np - the name of the device to check
2N/A * slblk - the starting offset of the device to check
2N/A * nblks - the number of blocks in the device to check
2N/A * OUTPUT: ep - return error pointer
2N/A * RETURNS: int - 0 - device contains soft partitions
2N/A * -1 - device does not contain soft partitions
2N/A * PURPOSE: determines whether a device contains any soft partitions
2N/A */
2N/A/* ARGSUSED */
2N/Aint
2N/Ameta_check_insp(
2N/A mdsetname_t *sp,
2N/A mdname_t *np,
2N/A diskaddr_t slblk,
2N/A diskaddr_t nblks,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A mdnamelist_t *spnlp = NULL; /* soft partition name list */
2N/A int count;
2N/A int rval;
2N/A
2N/A /* check set pointer */
2N/A assert(sp != NULL);
2N/A
2N/A /*
2N/A * Get a list of the soft partitions that currently reside on
2N/A * the component. We should ALWAYS force reload the cache,
2N/A * because if we're using the md.tab, we must rebuild
2N/A * the list because it won't contain the previous (if any)
2N/A * soft partition.
2N/A */
2N/A /* find all soft partitions on the component */
2N/A count = meta_sp_get_by_component(sp, np, &spnlp, 1, ep);
2N/A
2N/A if (count == -1) {
2N/A rval = -1;
2N/A } else if (count > 0) {
2N/A rval = mduseerror(ep, MDE_ALREADY, np->dev,
2N/A spnlp->namep->cname, np->cname);
2N/A } else {
2N/A rval = 0;
2N/A }
2N/A
2N/A metafreenamelist(spnlp);
2N/A return (rval);
2N/A}
2N/A
2N/A/*
2N/A * **************************************************************************
2N/A * Extent List Manipulation Functions *
2N/A * **************************************************************************
2N/A */
2N/A
2N/A/*
2N/A * FUNCTION: meta_sp_cmp_by_nameseq()
2N/A * INPUT: e1 - first node to compare
2N/A * e2 - second node to compare
2N/A * OUTPUT: none
2N/A * RETURNS: int - =0 - nodes are equal
2N/A * <0 - e1 should go before e2
2N/A * >0 - e1 should go after e2
2N/A * PURPOSE: used for sorted list inserts to build a list sorted by
2N/A * name first and sequence number second.
2N/A */
2N/Astatic int
2N/Ameta_sp_cmp_by_nameseq(sp_ext_node_t *e1, sp_ext_node_t *e2)
2N/A{
2N/A int rval;
2N/A
2N/A if (e1->ext_namep == NULL)
2N/A return (1);
2N/A if (e2->ext_namep == NULL)
2N/A return (-1);
2N/A if ((rval = strcmp(e1->ext_namep->cname, e2->ext_namep->cname)) != 0)
2N/A return (rval);
2N/A
2N/A /* the names are equal, compare sequence numbers */
2N/A if (e1->ext_seq > e2->ext_seq)
2N/A return (1);
2N/A if (e1->ext_seq < e2->ext_seq)
2N/A return (-1);
2N/A /* sequence numbers are also equal */
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * FUNCTION: meta_sp_cmp_by_offset()
2N/A * INPUT: e1 - first node to compare
2N/A * e2 - second node to compare
2N/A * OUTPUT: none
2N/A * RETURNS: int - =0 - nodes are equal
2N/A * <0 - e1 should go before e2
2N/A * >0 - e1 should go after e2
2N/A * PURPOSE: used for sorted list inserts to build a list sorted by offset
2N/A */
2N/Astatic int
2N/Ameta_sp_cmp_by_offset(sp_ext_node_t *e1, sp_ext_node_t *e2)
2N/A{
2N/A if (e1->ext_offset > e2->ext_offset)
2N/A return (1);
2N/A if (e1->ext_offset < e2->ext_offset)
2N/A return (-1);
2N/A /* offsets are equal */
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * FUNCTION: meta_sp_list_insert()
2N/A * INPUT: sp - the set name for the device the node belongs to
2N/A * np - the name of the device the node belongs to
2N/A * head - the head of the list, must be NULL for empty list
2N/A * offset - the physical offset of this extent in sectors
2N/A * length - the length of this extent in sectors
2N/A * type - the type of the extent being inserted
2N/A * seq - the sequence number of the extent being inserted
2N/A * flags - extent flags (eg. whether it needs to be updated)
2N/A * compare - the compare function to use
2N/A * OUTPUT: head - points to the new head if a node was inserted
2N/A * at the beginning
2N/A * RETURNS: void
2N/A * PURPOSE: inserts an extent node into a sorted doubly linked list.
2N/A * The sort order is determined by the compare function.
2N/A * Memory is allocated for the node in this function and it
2N/A * is up to the caller to free it, possibly using
2N/A * meta_sp_list_free(). If a node is inserted at the
2N/A * beginning of the list, the head pointer is updated to
2N/A * point to the new first node.
2N/A */
2N/Astatic void
2N/Ameta_sp_list_insert(
2N/A mdsetname_t *sp,
2N/A mdname_t *np,
2N/A sp_ext_node_t **head,
2N/A sp_ext_offset_t offset,
2N/A sp_ext_length_t length,
2N/A sp_ext_type_t type,
2N/A uint_t seq,
2N/A uint_t flags,
2N/A ext_cmpfunc_t compare
2N/A)
2N/A{
2N/A sp_ext_node_t *newext;
2N/A sp_ext_node_t *curext;
2N/A
2N/A assert(head != NULL);
2N/A
2N/A /* Don't bother adding zero length nodes */
2N/A if (length == 0ULL)
2N/A return;
2N/A
2N/A /* allocate and fill in new ext_node */
2N/A newext = Zalloc(sizeof (sp_ext_node_t));
2N/A
2N/A newext->ext_offset = offset;
2N/A newext->ext_length = length;
2N/A newext->ext_flags = flags;
2N/A newext->ext_type = type;
2N/A newext->ext_seq = seq;
2N/A newext->ext_setp = sp;
2N/A newext->ext_namep = np;
2N/A
2N/A /* first node in the list */
2N/A if (*head == NULL) {
2N/A newext->ext_next = newext->ext_prev = NULL;
2N/A *head = newext;
2N/A } else if ((*compare)(*head, newext) >= 0) {
2N/A /* the first node has a bigger offset, so insert before it */
2N/A assert((*head)->ext_prev == NULL);
2N/A
2N/A newext->ext_prev = NULL;
2N/A newext->ext_next = *head;
2N/A (*head)->ext_prev = newext;
2N/A *head = newext;
2N/A } else {
2N/A /*
2N/A * find the next node whose offset is greater than
2N/A * the one we want to insert, or the end of the list.
2N/A */
2N/A for (curext = *head;
2N/A (curext->ext_next != NULL) &&
2N/A ((*compare)(curext->ext_next, newext) < 0);
2N/A (curext = curext->ext_next))
2N/A ;
2N/A
2N/A /* link the new node in after the current node */
2N/A newext->ext_next = curext->ext_next;
2N/A newext->ext_prev = curext;
2N/A
2N/A if (curext->ext_next != NULL)
2N/A curext->ext_next->ext_prev = newext;
2N/A
2N/A curext->ext_next = newext;
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * FUNCTION: meta_sp_list_free()
2N/A * INPUT: head - the head of the list, must be NULL for empty list
2N/A * OUTPUT: head - points to NULL on return
2N/A * RETURNS: void
2N/A * PURPOSE: walks a double linked extent list and frees each node
2N/A */
2N/Astatic void
2N/Ameta_sp_list_free(sp_ext_node_t **head)
2N/A{
2N/A sp_ext_node_t *ext;
2N/A sp_ext_node_t *next;
2N/A
2N/A assert(head != NULL);
2N/A
2N/A ext = *head;
2N/A while (ext) {
2N/A next = ext->ext_next;
2N/A Free(ext);
2N/A ext = next;
2N/A }
2N/A *head = NULL;
2N/A}
2N/A
2N/A/*
2N/A * FUNCTION: meta_sp_list_remove()
2N/A * INPUT: head - the head of the list, must be NULL for empty list
2N/A * ext - the extent to remove, must be a member of the list
2N/A * OUTPUT: head - points to the new head of the list
2N/A * RETURNS: void
2N/A * PURPOSE: unlinks the node specified by ext from the list and
2N/A * frees it, possibly moving the head pointer forward if
2N/A * the head is the node being removed.
2N/A */
2N/Astatic void
2N/Ameta_sp_list_remove(sp_ext_node_t **head, sp_ext_node_t *ext)
2N/A{
2N/A assert(head != NULL);
2N/A assert(*head != NULL);
2N/A
2N/A if (*head == ext)
2N/A *head = ext->ext_next;
2N/A
2N/A if (ext->ext_prev != NULL)
2N/A ext->ext_prev->ext_next = ext->ext_next;
2N/A if (ext->ext_next != NULL)
2N/A ext->ext_next->ext_prev = ext->ext_prev;
2N/A Free(ext);
2N/A}
2N/A
2N/A/*
2N/A * FUNCTION: meta_sp_list_size()
2N/A * INPUT: head - the head of the list, must be NULL for empty list
2N/A * exttype - the type of the extents to sum
2N/A * exclude_wm - subtract space for extent headers from total
2N/A * OUTPUT: none
2N/A * RETURNS: sp_ext_length_t - the sum of all of the lengths
2N/A * PURPOSE: sums the lengths of all extents in the list matching the
2N/A * specified type. This could be used for computing the
2N/A * amount of free or used space, for example.
2N/A */
2N/Astatic sp_ext_length_t
2N/Ameta_sp_list_size(sp_ext_node_t *head, sp_ext_type_t exttype, int exclude_wm)
2N/A{
2N/A sp_ext_node_t *ext;
2N/A sp_ext_length_t size = 0LL;
2N/A
2N/A for (ext = head; ext != NULL; ext = ext->ext_next)
2N/A if (ext->ext_type == exttype)
2N/A size += ext->ext_length -
2N/A ((exclude_wm) ? MD_SP_WMSIZE : 0);
2N/A
2N/A return (size);
2N/A}
2N/A
2N/A/*
2N/A * FUNCTION: meta_sp_list_find()
2N/A * INPUT: head - the head of the list, must be NULL for empty list
2N/A * offset - the offset contained by the node to find
2N/A * OUTPUT: none
2N/A * RETURNS: sp_ext_node_t * - the node containing the requested offset
2N/A * or NULL if no such nodes were found.
2N/A * PURPOSE: finds a node in a list containing the requested offset
2N/A * (inclusive). If multiple nodes contain this offset then
2N/A * only the first will be returned, though typically these
2N/A * lists are managed with non-overlapping nodes.
2N/A *
2N/A * *The list MUST be sorted by offset for this function to work.*
2N/A */
2N/Astatic sp_ext_node_t *
2N/Ameta_sp_list_find(
2N/A sp_ext_node_t *head,
2N/A sp_ext_offset_t offset
2N/A)
2N/A{
2N/A sp_ext_node_t *ext;
2N/A
2N/A for (ext = head; ext != NULL; ext = ext->ext_next) {
2N/A /* check if the offset lies within this extent */
2N/A if ((offset >= ext->ext_offset) &&
2N/A (offset < ext->ext_offset + ext->ext_length)) {
2N/A /*
2N/A * the requested extent should always be a
2N/A * subset of an extent in the list.
2N/A */
2N/A return (ext);
2N/A }
2N/A }
2N/A return (NULL);
2N/A}
2N/A
2N/A/*
2N/A * FUNCTION: meta_sp_list_freefill()
2N/A * INPUT: head - the head of the list, must be NULL for empty list
2N/A * size - the size of the volume this extent list is
2N/A * representing
2N/A * OUTPUT: head - the new head of the list
2N/A * RETURNS: void
2N/A * PURPOSE: finds gaps in the extent list and fills them with a free
2N/A * node. If there is a gap at the beginning the head
2N/A * pointer will be changed to point to the new free node.
2N/A * If there is free space at the end, the last free extent
2N/A * will extend all the way out to the size specified.
2N/A *
2N/A * *The list MUST be sorted by offset for this function to work.*
2N/A */
2N/Astatic void
2N/Ameta_sp_list_freefill(
2N/A sp_ext_node_t **head,
2N/A sp_ext_length_t size
2N/A)
2N/A{
2N/A sp_ext_node_t *ext;
2N/A sp_ext_offset_t curoff = 0LL;
2N/A
2N/A for (ext = *head; ext != NULL; ext = ext->ext_next) {
2N/A if (curoff < ext->ext_offset)
2N/A meta_sp_list_insert(NULL, NULL, head,
2N/A curoff, ext->ext_offset - curoff,
2N/A EXTTYP_FREE, 0, 0, meta_sp_cmp_by_offset);
2N/A curoff = ext->ext_offset + ext->ext_length;
2N/A }
2N/A
2N/A /* pad inverse list out to the end */
2N/A if (curoff < size)
2N/A meta_sp_list_insert(NULL, NULL, head, curoff, size - curoff,
2N/A EXTTYP_FREE, 0, 0, meta_sp_cmp_by_offset);
2N/A
2N/A if (getenv(META_SP_DEBUG)) {
2N/A meta_sp_debug("meta_sp_list_freefill: Extent list with "
2N/A "holes freefilled:\n");
2N/A meta_sp_list_dump(*head);
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * FUNCTION: meta_sp_list_dump()
2N/A * INPUT: head - the head of the list, must be NULL for empty list
2N/A * OUTPUT: none
2N/A * RETURNS: void
2N/A * PURPOSE: dumps the entire extent list to stdout for easy debugging
2N/A */
2N/Astatic void
2N/Ameta_sp_list_dump(sp_ext_node_t *head)
2N/A{
2N/A sp_ext_node_t *ext;
2N/A
2N/A meta_sp_debug("meta_sp_list_dump: dumping extent list:\n");
2N/A meta_sp_debug("%5s %10s %5s %7s %10s %10s %5s %10s %10s\n", "Name",
2N/A "Addr", "Seq#", "Type", "Offset", "Length", "Flags", "Prev",
2N/A "Next");
2N/A for (ext = head; ext != NULL; ext = ext->ext_next) {
2N/A if (ext->ext_namep != NULL)
2N/A meta_sp_debug("%5s", ext->ext_namep->cname);
2N/A else
2N/A meta_sp_debug("%5s", "NONE");
2N/A
2N/A meta_sp_debug("%10p %5u ", (void *) ext, ext->ext_seq);
2N/A switch (ext->ext_type) {
2N/A case EXTTYP_ALLOC:
2N/A meta_sp_debug("%7s ", "ALLOC");
2N/A break;
2N/A case EXTTYP_FREE:
2N/A meta_sp_debug("%7s ", "FREE");
2N/A break;
2N/A case EXTTYP_END:
2N/A meta_sp_debug("%7s ", "END");
2N/A break;
2N/A case EXTTYP_RESERVED:
2N/A meta_sp_debug("%7s ", "RESV");
2N/A break;
2N/A default:
2N/A meta_sp_debug("%7s ", "INVLD");
2N/A break;
2N/A }
2N/A
2N/A meta_sp_debug("%10llu %10llu %5u %10p %10p\n",
2N/A ext->ext_offset, ext->ext_length,
2N/A ext->ext_flags, (void *) ext->ext_prev,
2N/A (void *) ext->ext_next);
2N/A }
2N/A meta_sp_debug("\n");
2N/A}
2N/A
2N/A/*
2N/A * FUNCTION: meta_sp_list_overlaps()
2N/A * INPUT: head - the head of the list, must be NULL for empty list
2N/A * OUTPUT: none
2N/A * RETURNS: int - 1 if extents overlap, 0 if ok
2N/A * PURPOSE: checks a list for overlaps. The list MUST be sorted by
2N/A * offset for this function to work properly.
2N/A */
2N/Astatic int
2N/Ameta_sp_list_overlaps(sp_ext_node_t *head)
2N/A{
2N/A sp_ext_node_t *ext;
2N/A
2N/A for (ext = head; ext->ext_next != NULL; ext = ext->ext_next) {
2N/A if (ext->ext_offset + ext->ext_length >
2N/A ext->ext_next->ext_offset)
2N/A return (1);
2N/A }
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * **************************************************************************
2N/A * Extent Allocation Functions *
2N/A * **************************************************************************
2N/A */
2N/A
2N/A/*
2N/A * FUNCTION: meta_sp_alloc_by_ext()
2N/A * INPUT: sp - the set name for the device the node belongs to
2N/A * np - the name of the device the node belongs to
2N/A * head - the head of the list, must be NULL for empty list
2N/A * free_ext - the free extent being allocated from
2N/A * alloc_offset - the offset of the allocation
2N/A * alloc_len - the length of the allocation
2N/A * seq - the sequence number of the allocation
2N/A * OUTPUT: head - the new head pointer
2N/A * RETURNS: void
2N/A * PURPOSE: allocates a portion of the free extent free_ext. The
2N/A * allocated portion starts at alloc_offset and is
2N/A * alloc_length long. Both (alloc_offset) and (alloc_offset +
2N/A * alloc_length) must be contained within the free extent.
2N/A *
2N/A * The free extent is split into as many as 3 pieces - a
2N/A * free extent containing [ free_offset .. alloc_offset ), an
2N/A * allocated extent containing the range [ alloc_offset ..
2N/A * alloc_end ], and another free extent containing the
2N/A * range ( alloc_end .. free_end ]. If either of the two
2N/A * new free extents would be zero length, they are not created.
2N/A *
2N/A * Finally, the original free extent is removed. All newly
2N/A * created extents have the EXTFLG_UPDATE flag set.
2N/A */
2N/Astatic void
2N/Ameta_sp_alloc_by_ext(
2N/A mdsetname_t *sp,
2N/A mdname_t *np,
2N/A sp_ext_node_t **head,
2N/A sp_ext_node_t *free_ext,
2N/A sp_ext_offset_t alloc_offset,
2N/A sp_ext_length_t alloc_length,
2N/A uint_t seq
2N/A)
2N/A{
2N/A sp_ext_offset_t free_offset = free_ext->ext_offset;
2N/A sp_ext_length_t free_length = free_ext->ext_length;
2N/A
2N/A sp_ext_offset_t alloc_end = alloc_offset + alloc_length;
2N/A sp_ext_offset_t free_end = free_offset + free_length;
2N/A
2N/A /* allocated extent must be a subset of the free extent */
2N/A assert(free_offset <= alloc_offset);
2N/A assert(free_end >= alloc_end);
2N/A
2N/A meta_sp_list_remove(head, free_ext);
2N/A
2N/A if (free_offset < alloc_offset) {
2N/A meta_sp_list_insert(NULL, NULL, head, free_offset,
2N/A (alloc_offset - free_offset), EXTTYP_FREE, 0,
2N/A EXTFLG_UPDATE, meta_sp_cmp_by_offset);
2N/A }
2N/A
2N/A if (free_end > alloc_end) {
2N/A meta_sp_list_insert(NULL, NULL, head, alloc_end,
2N/A (free_end - alloc_end), EXTTYP_FREE, 0, EXTFLG_UPDATE,
2N/A meta_sp_cmp_by_offset);
2N/A }
2N/A
2N/A meta_sp_list_insert(sp, np, head, alloc_offset, alloc_length,
2N/A EXTTYP_ALLOC, seq, EXTFLG_UPDATE, meta_sp_cmp_by_offset);
2N/A
2N/A if (getenv(META_SP_DEBUG)) {
2N/A meta_sp_debug("meta_sp_alloc_by_ext: extent list:\n");
2N/A meta_sp_list_dump(*head);
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * FUNCTION: meta_sp_alloc_by_len()
2N/A * INPUT: sp - the set name for the device the node belongs to
2N/A * np - the name of the device the node belongs to
2N/A * head - the head of the list, must be NULL for empty list
2N/A * *lp - the requested length to allocate
2N/A * last_off - the last offset already allocated.
2N/A * alignment - the desired extent alignmeent
2N/A * OUTPUT: head - the new head pointer
2N/A * *lp - the length allocated
2N/A * RETURNS: int - -1 if error, the number of new extents on success
2N/A * PURPOSE: allocates extents from free space to satisfy the requested
2N/A * length. If requested length is zero, allocates all
2N/A * remaining free space. This function provides the meat
2N/A * of the extent allocation algorithm. Allocation is a
2N/A * three tier process:
2N/A *
2N/A * 1. If last_off is nonzero and there is free space following
2N/A * that node, then it is extended to allocate as much of that
2N/A * free space as possible. This is useful for metattach.
2N/A * 2. If a free extent can be found to satisfy the remaining
2N/A * requested space, then satisfy the rest of the request
2N/A * from that extent.
2N/A * 3. Start allocating space from any remaining free extents until
2N/A * the remainder of the request is satisified.
2N/A *
2N/A * If alignment is non-zero, then every extent modified
2N/A * or newly allocated will be aligned modulo alignment,
2N/A * with a length that is an integer multiple of
2N/A * alignment.
2N/A *
2N/A * The EXTFLG_UPDATE flag is set for all nodes (free and
2N/A * allocated) that require updated watermarks.
2N/A *
2N/A * This algorithm may have a negative impact on fragmentation
2N/A * in pathological cases and may be improved if it turns out
2N/A * to be a problem. This may be exacerbated by particularly
2N/A * large alignments.
2N/A *
2N/A * NOTE: It's confusing, so it demands an explanation:
2N/A * - len is used to represent requested data space; it
2N/A * does not include room for a watermark. On each full
2N/A * or partial allocation, len will be decremented by
2N/A * alloc_len (see next paragraph) until it reaches
2N/A * zero.
2N/A * - alloc_len is used to represent data space allocated
2N/A * from a particular extent; it does not include space
2N/A * for a watermark. In the rare event that a_length
2N/A * (see next paragraph) is equal to MD_SP_WMSIZE,
2N/A * alloc_len will be zero and the resulting MD_SP_WMSIZE
2N/A * fragment of space will be utterly unusable.
2N/A * - a_length is used to represent all space to be
2N/A * allocated from a particular extent; it DOES include
2N/A * space for a watermark.
2N/A */
2N/Astatic int
2N/Ameta_sp_alloc_by_len(
2N/A mdsetname_t *sp,
2N/A mdname_t *np,
2N/A sp_ext_node_t **head,
2N/A sp_ext_length_t *lp,
2N/A sp_ext_offset_t last_off,
2N/A sp_ext_offset_t alignment
2N/A)
2N/A{
2N/A sp_ext_node_t *free_ext;
2N/A sp_ext_node_t *alloc_ext;
2N/A uint_t last_seq = 0;
2N/A uint_t numexts = 0;
2N/A sp_ext_length_t freespace;
2N/A sp_ext_length_t alloc_len;
2N/A sp_ext_length_t len;
2N/A
2N/A /* We're DOA if we can't read *lp */
2N/A assert(lp != NULL);
2N/A len = *lp;
2N/A
2N/A /*
2N/A * Process the nominal case first: we've been given an actual
2N/A * size argument, rather than the literal "all"
2N/A */
2N/A
2N/A if (len != 0) {
2N/A
2N/A /*
2N/A * Short circuit the check for free space. This may
2N/A * tell us we have enough space when we really don't
2N/A * because each extent loses space to a watermark, but
2N/A * it will always tell us there isn't enough space
2N/A * correctly. Worst case we do some extra work.
2N/A */
2N/A freespace = meta_sp_list_size(*head, EXTTYP_FREE,
2N/A INCLUDE_WM);
2N/A
2N/A if (freespace < len)
2N/A return (-1);
2N/A
2N/A /*
2N/A * First see if we can extend the last extent for an
2N/A * attach.
2N/A */
2N/A if (last_off != 0LL) {
2N/A int align = 0;
2N/A
2N/A alloc_ext =
2N/A meta_sp_list_find(*head, last_off);
2N/A assert(alloc_ext != NULL);
2N/A
2N/A /*
2N/A * The offset test reflects the
2N/A * inclusion of the watermark in the extent
2N/A */
2N/A align = (alignment > 0) &&
2N/A (((alloc_ext->ext_offset + MD_SP_WMSIZE) %
2N/A alignment) == 0);
2N/A
2N/A /*
2N/A * If we decided not to align here, we should
2N/A * also reset "alignment" so we don't bother
2N/A * later, either.
2N/A */
2N/A if (!align) {
2N/A alignment = 0;
2N/A }
2N/A
2N/A last_seq = alloc_ext->ext_seq;
2N/A
2N/A free_ext = meta_sp_list_find(*head,
2N/A alloc_ext->ext_offset +
2N/A alloc_ext->ext_length);
2N/A
2N/A /*
2N/A * If a free extent follows our last allocated
2N/A * extent, then remove the last allocated
2N/A * extent and increase the size of the free
2N/A * extent to overlap it, then allocate the
2N/A * total space from the new free extent.
2N/A */
2N/A if (free_ext != NULL &&
2N/A free_ext->ext_type == EXTTYP_FREE) {
2N/A assert(free_ext->ext_offset ==
2N/A alloc_ext->ext_offset +
2N/A alloc_ext->ext_length);
2N/A
2N/A alloc_len =
2N/A MIN(len, free_ext->ext_length);
2N/A
2N/A if (align && (alloc_len < len)) {
2N/A /* No watermark space needed */
2N/A alloc_len -= alloc_len % alignment;
2N/A }
2N/A
2N/A if (alloc_len > 0) {
2N/A free_ext->ext_offset -=
2N/A alloc_ext->ext_length;
2N/A free_ext->ext_length +=
2N/A alloc_ext->ext_length;
2N/A
2N/A meta_sp_alloc_by_ext(sp, np, head,
2N/A free_ext, free_ext->ext_offset,
2N/A alloc_ext->ext_length + alloc_len,
2N/A last_seq);
2N/A
2N/A /*
2N/A * now remove the original allocated
2N/A * node. We may have overlapping
2N/A * extents for a short time before
2N/A * this node is removed.
2N/A */
2N/A meta_sp_list_remove(head, alloc_ext);
2N/A len -= alloc_len;
2N/A }
2N/A }
2N/A last_seq++;
2N/A }
2N/A
2N/A if (len == 0LL)
2N/A goto out;
2N/A
2N/A /*
2N/A * Next, see if we can find a single allocation for
2N/A * the remainder. This may make fragmentation worse
2N/A * in some cases, but there's no good way to allocate
2N/A * that doesn't have a highly fragmented corner case.
2N/A */
2N/A for (free_ext = *head; free_ext != NULL;
2N/A free_ext = free_ext->ext_next) {
2N/A sp_ext_offset_t a_offset;
2N/A sp_ext_offset_t a_length;
2N/A
2N/A if (free_ext->ext_type != EXTTYP_FREE)
2N/A continue;
2N/A
2N/A /*
2N/A * The length test should include space for
2N/A * the watermark
2N/A */
2N/A
2N/A a_offset = free_ext->ext_offset;
2N/A a_length = free_ext->ext_length;
2N/A
2N/A if (alignment > 0) {
2N/A
2N/A /*
2N/A * Shortcut for extents that have been
2N/A * previously added to pad out the
2N/A * data space
2N/A */
2N/A if (a_length < alignment) {
2N/A continue;
2N/A }
2N/A
2N/A /*
2N/A * Round up so the data space begins
2N/A * on a properly aligned boundary.
2N/A */
2N/A a_offset += alignment -
2N/A (a_offset % alignment) - MD_SP_WMSIZE;
2N/A
2N/A /*
2N/A * This is only necessary in case the
2N/A * watermark size is ever greater than
2N/A * one. It'll never happen, of
2N/A * course; we'll get rid of watermarks
2N/A * before we make 'em bigger.
2N/A */
2N/A if (a_offset < free_ext->ext_offset) {
2N/A a_offset += alignment;
2N/A }
2N/A
2N/A /*
2N/A * Adjust the length to account for
2N/A * the space lost above (if any)
2N/A */
2N/A a_length -=
2N/A (a_offset - free_ext->ext_offset);
2N/A }
2N/A
2N/A if (a_length >= len + MD_SP_WMSIZE) {
2N/A meta_sp_alloc_by_ext(sp, np, head,
2N/A free_ext, a_offset,
2N/A len + MD_SP_WMSIZE, last_seq);
2N/A
2N/A len = 0LL;
2N/A numexts++;
2N/A break;
2N/A }
2N/A }
2N/A
2N/A if (len == 0LL)
2N/A goto out;
2N/A
2N/A
2N/A /*
2N/A * If the request could not be satisfied by extending
2N/A * the last extent or by a single extent, then put
2N/A * multiple smaller extents together until the request
2N/A * is satisfied.
2N/A */
2N/A for (free_ext = *head; (free_ext != NULL) && (len > 0);
2N/A free_ext = free_ext->ext_next) {
2N/A sp_ext_offset_t a_offset;
2N/A sp_ext_length_t a_length;
2N/A
2N/A if (free_ext->ext_type != EXTTYP_FREE)
2N/A continue;
2N/A
2N/A a_offset = free_ext->ext_offset;
2N/A a_length = free_ext->ext_length;
2N/A
2N/A if (alignment > 0) {
2N/A
2N/A /*
2N/A * Shortcut for extents that have been
2N/A * previously added to pad out the
2N/A * data space
2N/A */
2N/A if (a_length < alignment) {
2N/A continue;
2N/A }
2N/A
2N/A /*
2N/A * Round up so the data space begins
2N/A * on a properly aligned boundary.
2N/A */
2N/A a_offset += alignment -
2N/A (a_offset % alignment) - MD_SP_WMSIZE;
2N/A
2N/A /*
2N/A * This is only necessary in case the
2N/A * watermark size is ever greater than
2N/A * one. It'll never happen, of
2N/A * course; we'll get rid of watermarks
2N/A * before we make 'em bigger.
2N/A */
2N/A if (a_offset < free_ext->ext_offset) {
2N/A a_offset += alignment;
2N/A }
2N/A
2N/A /*
2N/A * Adjust the length to account for
2N/A * the space lost above (if any)
2N/A */
2N/A a_length -=
2N/A (a_offset - free_ext->ext_offset);
2N/A
2N/A /*
2N/A * Adjust the length to be properly
2N/A * aligned if it is NOT to be the
2N/A * last extent in the soft partition.
2N/A */
2N/A if ((a_length - MD_SP_WMSIZE) < len)
2N/A a_length -=
2N/A (a_length - MD_SP_WMSIZE)
2N/A % alignment;
2N/A }
2N/A
2N/A alloc_len = MIN(len, a_length - MD_SP_WMSIZE);
2N/A if (alloc_len == 0)
2N/A continue;
2N/A
2N/A /*
2N/A * meta_sp_alloc_by_ext() expects the
2N/A * allocation length to include the watermark
2N/A * size, which is why we don't simply pass in
2N/A * alloc_len here.
2N/A */
2N/A meta_sp_alloc_by_ext(sp, np, head, free_ext,
2N/A a_offset, MIN(len + MD_SP_WMSIZE, a_length),
2N/A last_seq);
2N/A
2N/A len -= alloc_len;
2N/A numexts++;
2N/A last_seq++;
2N/A }
2N/A
2N/A
2N/A /*
2N/A * If there was not enough space we can throw it all
2N/A * away since no real work has been done yet.
2N/A */
2N/A if (len != 0) {
2N/A meta_sp_list_free(head);
2N/A return (-1);
2N/A }
2N/A }
2N/A
2N/A /*
2N/A * Otherwise, the literal "all" was specified: allocate all
2N/A * available free space. Don't bother with alignment.
2N/A */
2N/A else {
2N/A /* First, extend the last extent if this is a grow */
2N/A if (last_off != 0LL) {
2N/A alloc_ext =
2N/A meta_sp_list_find(*head, last_off);
2N/A assert(alloc_ext != NULL);
2N/A
2N/A last_seq = alloc_ext->ext_seq;
2N/A
2N/A free_ext = meta_sp_list_find(*head,
2N/A alloc_ext->ext_offset +
2N/A alloc_ext->ext_length);
2N/A
2N/A /*
2N/A * If a free extent follows our last allocated
2N/A * extent, then remove the last allocated
2N/A * extent and increase the size of the free
2N/A * extent to overlap it, then allocate the
2N/A * total space from the new free extent.
2N/A */
2N/A if (free_ext != NULL &&
2N/A free_ext->ext_type == EXTTYP_FREE) {
2N/A assert(free_ext->ext_offset ==
2N/A alloc_ext->ext_offset +
2N/A alloc_ext->ext_length);
2N/A
2N/A len = alloc_len =
2N/A free_ext->ext_length;
2N/A
2N/A free_ext->ext_offset -=
2N/A alloc_ext->ext_length;
2N/A free_ext->ext_length +=
2N/A alloc_ext->ext_length;
2N/A
2N/A meta_sp_alloc_by_ext(sp, np, head,
2N/A free_ext, free_ext->ext_offset,
2N/A alloc_ext->ext_length + alloc_len,
2N/A last_seq);
2N/A
2N/A /*
2N/A * now remove the original allocated
2N/A * node. We may have overlapping
2N/A * extents for a short time before
2N/A * this node is removed.
2N/A */
2N/A meta_sp_list_remove(head, alloc_ext);
2N/A }
2N/A
2N/A last_seq++;
2N/A }
2N/A
2N/A /* Next, grab all remaining free space */
2N/A for (free_ext = *head; free_ext != NULL;
2N/A free_ext = free_ext->ext_next) {
2N/A
2N/A if (free_ext->ext_type == EXTTYP_FREE) {
2N/A alloc_len =
2N/A free_ext->ext_length - MD_SP_WMSIZE;
2N/A if (alloc_len == 0)
2N/A continue;
2N/A
2N/A /*
2N/A * meta_sp_alloc_by_ext() expects the
2N/A * allocation length to include the
2N/A * watermark size, which is why we
2N/A * don't simply pass in alloc_len
2N/A * here.
2N/A */
2N/A meta_sp_alloc_by_ext(sp, np, head,
2N/A free_ext, free_ext->ext_offset,
2N/A free_ext->ext_length,
2N/A last_seq);
2N/A
2N/A len += alloc_len;
2N/A numexts++;
2N/A last_seq++;
2N/A }
2N/A }
2N/A }
2N/A
2N/Aout:
2N/A if (getenv(META_SP_DEBUG)) {
2N/A meta_sp_debug("meta_sp_alloc_by_len: Extent list after "
2N/A "allocation:\n");
2N/A meta_sp_list_dump(*head);
2N/A }
2N/A
2N/A if (*lp == 0) {
2N/A *lp = len;
2N/A
2N/A /*
2N/A * Make sure the callers hit a no space error if we
2N/A * didn't actually find anything.
2N/A */
2N/A if (len == 0) {
2N/A return (-1);
2N/A }
2N/A }
2N/A
2N/A return (numexts);
2N/A}
2N/A
2N/A/*
2N/A * FUNCTION: meta_sp_alloc_by_list()
2N/A * INPUT: sp - the set name for the device the node belongs to
2N/A * np - the name of the device the node belongs to
2N/A * head - the head of the list, must be NULL for empty list
2N/A * oblist - an extent list containing requested nodes to allocate
2N/A * OUTPUT: head - the new head pointer
2N/A * RETURNS: int - -1 if error, the number of new extents on success
2N/A * PURPOSE: allocates extents from free space to satisfy the requested
2N/A * extent list. This is primarily used for the -o/-b options
2N/A * where the user may specifically request extents to allocate.
2N/A * Each extent in the oblist must be a subset (inclusive) of a
2N/A * free extent and may not overlap each other. This
2N/A * function sets the EXTFLG_UPDATE flag for each node that
2N/A * requires a watermark update after allocating.
2N/A */
2N/Astatic int
2N/Ameta_sp_alloc_by_list(
2N/A mdsetname_t *sp,
2N/A mdname_t *np,
2N/A sp_ext_node_t **head,
2N/A sp_ext_node_t *oblist
2N/A)
2N/A{
2N/A sp_ext_node_t *ext;
2N/A sp_ext_node_t *free_ext;
2N/A uint_t numexts = 0;
2N/A
2N/A for (ext = oblist; ext != NULL; ext = ext->ext_next) {
2N/A
2N/A free_ext = meta_sp_list_find(*head,
2N/A ext->ext_offset - MD_SP_WMSIZE);
2N/A
2N/A /* Make sure the allocation is within the free extent */
2N/A if ((free_ext == NULL) ||
2N/A (ext->ext_offset + ext->ext_length >
2N/A free_ext->ext_offset + free_ext->ext_length) ||
2N/A (free_ext->ext_type != EXTTYP_FREE))
2N/A return (-1);
2N/A
2N/A meta_sp_alloc_by_ext(sp, np, head, free_ext,
2N/A ext->ext_offset - MD_SP_WMSIZE,
2N/A ext->ext_length + MD_SP_WMSIZE, ext->ext_seq);
2N/A
2N/A numexts++;
2N/A }
2N/A
2N/A assert(meta_sp_list_overlaps(*head) == 0);
2N/A
2N/A if (getenv(META_SP_DEBUG)) {
2N/A meta_sp_debug("meta_sp_alloc_by_list: Extent list after "
2N/A "allocation:\n");
2N/A meta_sp_list_dump(*head);
2N/A }
2N/A
2N/A return (numexts);
2N/A}
2N/A
2N/A/*
2N/A * **************************************************************************
2N/A * Extent List Population Functions *
2N/A * **************************************************************************
2N/A */
2N/A
2N/A/*
2N/A * FUNCTION: meta_sp_extlist_from_namelist()
2N/A * INPUT: sp - the set name for the device the node belongs to
2N/A * spnplp - the namelist of soft partitions to build a list from
2N/A * OUTPUT: extlist - the extent list built from the SPs in the namelist
2N/A * ep - return error pointer
2N/A * RETURNS: int - -1 if error, 0 on success
2N/A * PURPOSE: builds an extent list representing the soft partitions
2N/A * specified in the namelist. Each extent in each soft
2N/A * partition is added to the list with the type EXTTYP_ALLOC.
2N/A * The EXTFLG_UPDATE flag is not set on any nodes. Each
2N/A * extent in the list includes the space occupied by the
2N/A * watermark, which is not included in the unit structures.
2N/A */
2N/Astatic int
2N/Ameta_sp_extlist_from_namelist(
2N/A mdsetname_t *sp,
2N/A mdnamelist_t *spnlp,
2N/A sp_ext_node_t **extlist,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A int extn;
2N/A md_sp_t *msp; /* unit structure of the sp's */
2N/A mdnamelist_t *namep;
2N/A
2N/A assert(sp != NULL);
2N/A
2N/A /*
2N/A * Now go through the soft partitions and add a node to the used
2N/A * list for each allocated extent.
2N/A */
2N/A for (namep = spnlp; namep != NULL; namep = namep->next) {
2N/A mdname_t *curnp = namep->namep;
2N/A
2N/A /* get the unit structure */
2N/A if ((msp = meta_get_sp_common(sp, curnp, 0, ep)) == NULL)
2N/A return (-1);
2N/A
2N/A for (extn = 0; (extn < msp->ext.ext_len); extn++) {
2N/A md_sp_ext_t *extp = &msp->ext.ext_val[extn];
2N/A
2N/A /*
2N/A * subtract from offset and add to the length
2N/A * to account for the watermark, which is not
2N/A * contained in the extents in the unit structure.
2N/A */
2N/A meta_sp_list_insert(sp, curnp, extlist,
2N/A extp->poff - MD_SP_WMSIZE, extp->len + MD_SP_WMSIZE,
2N/A EXTTYP_ALLOC, extn, 0, meta_sp_cmp_by_offset);
2N/A }
2N/A }
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * FUNCTION: meta_sp_extlist_from_wm()
2N/A * INPUT: sp - the set name for the device the node belongs to
2N/A * compnp - the name of the device to scan watermarks on
2N/A * OUTPUT: extlist - the extent list built from the SPs in the namelist
2N/A * ep - return error pointer
2N/A * RETURNS: int - -1 if error, 0 on success
2N/A * PURPOSE: builds an extent list representing the soft partitions
2N/A * specified in the namelist. Each extent in each soft
2N/A * partition is added to the list with the type EXTTYP_ALLOC.
2N/A * The EXTFLG_UPDATE flag is not set on any nodes. Each
2N/A * extent in the list includes the space occupied by the
2N/A * watermark, which is not included in the unit structures.
2N/A */
2N/Astatic int
2N/Ameta_sp_extlist_from_wm(
2N/A mdsetname_t *sp,
2N/A mdname_t *compnp,
2N/A sp_ext_node_t **extlist,
2N/A ext_cmpfunc_t compare,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A mp_watermark_t wm;
2N/A mdname_t *np = NULL;
2N/A mdsetname_t *spsetp = NULL;
2N/A sp_ext_offset_t cur_off;
2N/A md_set_desc *sd;
2N/A int init = 0;
2N/A mdkey_t key;
2N/A minor_t mnum;
2N/A
2N/A if (!metaislocalset(sp)) {
2N/A if ((sd = metaget_setdesc(sp, ep)) == NULL)
2N/A return (-1);
2N/A }
2N/A
2N/A if ((cur_off = meta_sp_get_start(sp, compnp, ep)) == MD_DISKADDR_ERROR)
2N/A return (-1);
2N/A
2N/A for (;;) {
2N/A if (meta_sp_read_wm(sp, compnp, &wm, cur_off, ep) != 0) {
2N/A return (-1);
2N/A }
2N/A
2N/A /* get the set and name pointers */
2N/A if (strcmp(wm.wm_setname, MD_SP_LOCALSETNAME) != 0) {
2N/A if ((spsetp = metasetname(wm.wm_setname, ep)) == NULL) {
2N/A return (-1);
2N/A }
2N/A }
2N/A
2N/A /*
2N/A * For the MN set, meta_init_make_device needs to
2N/A * be run on all the nodes so the entries for the
2N/A * softpart device name and its comp can be created
2N/A * in the same order in the replica namespace. If
2N/A * we have it run on mdmn_do_iocset then the mddbs
2N/A * will be out of sync between master node and slave
2N/A * nodes.
2N/A */
2N/A if (strcmp(wm.wm_mdname, MD_SP_FREEWMNAME) != 0) {
2N/A
2N/A if (!metaislocalset(sp) && MD_MNSET_DESC(sd)) {
2N/A md_mn_msg_addmdname_t *send_params;
2N/A int result;
2N/A md_mn_result_t *resp = NULL;
2N/A int message_size;
2N/A
2N/A message_size = sizeof (*send_params) +
2N/A strlen(wm.wm_mdname) + 1;
2N/A send_params = Zalloc(message_size);
2N/A send_params->addmdname_setno = sp->setno;
2N/A (void) strcpy(&send_params->addmdname_name[0],
2N/A wm.wm_mdname);
2N/A result = mdmn_send_message(sp->setno,
2N/A MD_MN_MSG_ADDMDNAME,
2N/A MD_MSGF_PANIC_WHEN_INCONSISTENT, 0,
2N/A (char *)send_params, message_size, &resp,
2N/A ep);
2N/A Free(send_params);
2N/A if (resp != NULL) {
2N/A if (resp->mmr_exitval != 0) {
2N/A free_result(resp);
2N/A return (-1);
2N/A }
2N/A free_result(resp);
2N/A }
2N/A if (result != 0)
2N/A return (-1);
2N/A } else {
2N/A
2N/A if (!is_existing_meta_hsp(sp, wm.wm_mdname)) {
2N/A if ((key = meta_init_make_device(&sp,
2N/A wm.wm_mdname, ep)) <= 0) {
2N/A return (-1);
2N/A }
2N/A init = 1;
2N/A }
2N/A }
2N/A
2N/A np = metaname(&spsetp, wm.wm_mdname, META_DEVICE, ep);
2N/A if (np == NULL) {
2N/A if (init) {
2N/A if (meta_getnmentbykey(sp->setno,
2N/A MD_SIDEWILD, key, NULL, &mnum,
2N/A NULL, ep) != NULL) {
2N/A (void) metaioctl(MD_IOCREM_DEV,
2N/A &mnum, ep, NULL);
2N/A }
2N/A (void) del_self_name(sp, key, ep);
2N/A }
2N/A return (-1);
2N/A }
2N/A }
2N/A
2N/A /* insert watermark into extent list */
2N/A meta_sp_list_insert(spsetp, np, extlist, cur_off,
2N/A wm.wm_length + MD_SP_WMSIZE, wm.wm_type, wm.wm_seq,
2N/A EXTFLG_UPDATE, compare);
2N/A
2N/A /* if we see the end watermark, we're done */
2N/A if (wm.wm_type == EXTTYP_END)
2N/A break;
2N/A
2N/A cur_off += wm.wm_length + 1;
2N/A
2N/A /* clear out set and name pointers for next iteration */
2N/A np = NULL;
2N/A spsetp = NULL;
2N/A }
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * **************************************************************************
2N/A * Print (metastat) Functions *
2N/A * **************************************************************************
2N/A */
2N/A
2N/A/*
2N/A * FUNCTION: meta_sp_short_print()
2N/A * INPUT: msp - the unit structure to display
2N/A * fp - the file pointer to send output to
2N/A * options - print options from the command line processor
2N/A * OUTPUT: ep - return error pointer
2N/A * RETURNS: int - -1 if error, 0 on success
2N/A * PURPOSE: display a short report of the soft partition in md.tab
2N/A * form, primarily used for metastat -p.
2N/A */
2N/Astatic int
2N/Ameta_sp_short_print(
2N/A md_sp_t *msp,
2N/A char *fname,
2N/A FILE *fp,
2N/A mdprtopts_t options,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A int extn;
2N/A
2N/A if (options & PRINT_LARGEDEVICES) {
2N/A if ((msp->common.revision & MD_64BIT_META_DEV) == 0)
2N/A return (0);
2N/A }
2N/A
2N/A if (options & PRINT_FN) {
2N/A if ((msp->common.revision & MD_FN_META_DEV) == 0)
2N/A return (0);
2N/A }
2N/A
2N/A /* print name and -p */
2N/A if (fprintf(fp, "%s -p", msp->common.namep->cname) == EOF)
2N/A return (mdsyserror(ep, errno, fname));
2N/A
2N/A /* print the component */
2N/A /*
2N/A * Always print the full path name
2N/A */
2N/A if (fprintf(fp, " %s", msp->compnamep->rname) == EOF)
2N/A return (mdsyserror(ep, errno, fname));
2N/A
2N/A /* print out each extent */
2N/A for (extn = 0; (extn < msp->ext.ext_len); extn++) {
2N/A md_sp_ext_t *extp = &msp->ext.ext_val[extn];
2N/A if (fprintf(fp, " -o %llu -b %llu ", extp->poff,
2N/A extp->len) == EOF)
2N/A return (mdsyserror(ep, errno, fname));
2N/A }
2N/A
2N/A if (fprintf(fp, "\n") == EOF)
2N/A return (mdsyserror(ep, errno, fname));
2N/A
2N/A /* success */
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * FUNCTION: meta_sp_status_to_name()
2N/A * INPUT: xsp_status - the status value to convert to a string
2N/A * tstate - transient errored device state. If set the
2N/A * device is Unavailable
2N/A * OUTPUT: none
2N/A * RETURNS: char * - a pointer to the string representing the status value
2N/A * PURPOSE: return an internationalized string representing the
2N/A * status value for a soft partition. The strings are
2N/A * strdup'd and must be freed by the caller.
2N/A */
2N/Astatic char *
2N/Ameta_sp_status_to_name(
2N/A xsp_status_t xsp_status,
2N/A uint_t tstate
2N/A)
2N/A{
2N/A char *rval = NULL;
2N/A
2N/A /*
2N/A * Check to see if we have MD_INACCESSIBLE set. This is the only valid
2N/A * value for an 'Unavailable' return. tstate can be set because of
2N/A * other multi-node reasons (e.g. ABR being set)
2N/A */
2N/A if (tstate & MD_INACCESSIBLE) {
2N/A return (Strdup(dgettext(TEXT_DOMAIN, "Unavailable")));
2N/A }
2N/A
2N/A switch (xsp_status) {
2N/A case MD_SP_CREATEPEND:
2N/A rval = Strdup(dgettext(TEXT_DOMAIN, "Creating"));
2N/A break;
2N/A case MD_SP_GROWPEND:
2N/A rval = Strdup(dgettext(TEXT_DOMAIN, "Growing"));
2N/A break;
2N/A case MD_SP_DELPEND:
2N/A rval = Strdup(dgettext(TEXT_DOMAIN, "Deleting"));
2N/A break;
2N/A case MD_SP_OK:
2N/A rval = Strdup(dgettext(TEXT_DOMAIN, "Okay"));
2N/A break;
2N/A case MD_SP_ERR:
2N/A rval = Strdup(dgettext(TEXT_DOMAIN, "Errored"));
2N/A break;
2N/A case MD_SP_RECOVER:
2N/A rval = Strdup(dgettext(TEXT_DOMAIN, "Recovering"));
2N/A break;
2N/A }
2N/A
2N/A if (rval == NULL)
2N/A rval = Strdup(dgettext(TEXT_DOMAIN, "Invalid"));
2N/A
2N/A return (rval);
2N/A}
2N/A
2N/A/*
2N/A * FUNCTION: meta_sp_report()
2N/A * INPUT: sp - the set name for the unit being displayed
2N/A * msp - the unit structure to display
2N/A * nlpp - pass back the large devs
2N/A * fp - the file pointer to send output to
2N/A * options - print options from the command line processor
2N/A * OUTPUT: ep - return error pointer
2N/A * RETURNS: int - -1 if error, 0 on success
2N/A * PURPOSE: print a full report of the device specified
2N/A */
2N/Astatic int
2N/Ameta_sp_report(
2N/A mdsetname_t *sp,
2N/A md_sp_t *msp,
2N/A mdnamelist_t **nlpp,
2N/A char *fname,
2N/A FILE *fp,
2N/A mdprtopts_t options,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A uint_t extn;
2N/A char *status;
2N/A char *devid = "";
2N/A mdname_t *didnp = NULL;
2N/A ddi_devid_t dtp;
2N/A int len;
2N/A uint_t tstate = 0;
2N/A
2N/A if (options & PRINT_LARGEDEVICES) {
2N/A if ((msp->common.revision & MD_64BIT_META_DEV) == 0) {
2N/A return (0);
2N/A } else {
2N/A if (meta_getdevs(sp, msp->common.namep, nlpp, ep) != 0)
2N/A return (-1);
2N/A }
2N/A }
2N/A
2N/A if (options & PRINT_FN) {
2N/A if ((msp->common.revision & MD_FN_META_DEV) == 0) {
2N/A return (0);
2N/A } else {
2N/A if (meta_getdevs(sp, msp->common.namep, nlpp, ep) != 0)
2N/A return (-1);
2N/A }
2N/A }
2N/A
2N/A if (options & PRINT_HEADER) {
2N/A if (fprintf(fp, dgettext(TEXT_DOMAIN, "%s: Soft Partition\n"),
2N/A msp->common.namep->cname) == EOF)
2N/A return (mdsyserror(ep, errno, fname));
2N/A }
2N/A
2N/A if (fprintf(fp, dgettext(TEXT_DOMAIN, " Device: %s\n"),
2N/A msp->compnamep->cname) == EOF)
2N/A return (mdsyserror(ep, errno, fname));
2N/A
2N/A /* Determine if device is available before displaying status */
2N/A if (metaismeta(msp->common.namep)) {
2N/A if (meta_get_tstate(msp->common.namep->dev, &tstate, ep) != 0)
2N/A return (-1);
2N/A }
2N/A status = meta_sp_status_to_name(msp->status, tstate & MD_DEV_ERRORED);
2N/A
2N/A /* print out "State" to be consistent with other metadevices */
2N/A if (tstate & MD_ABR_CAP) {
2N/A if (fprintf(fp, dgettext(TEXT_DOMAIN,
2N/A " State: %s - Application Based Recovery (ABR)\n"),
2N/A status) == EOF) {
2N/A Free(status);
2N/A return (mdsyserror(ep, errno, fname));
2N/A }
2N/A } else {
2N/A if (fprintf(fp, dgettext(TEXT_DOMAIN,
2N/A " State: %s\n"), status) == EOF) {
2N/A Free(status);
2N/A return (mdsyserror(ep, errno, fname));
2N/A }
2N/A }
2N/A free(status);
2N/A
2N/A if (fprintf(fp, dgettext(TEXT_DOMAIN, " Size: %llu blocks (%s)\n"),
2N/A msp->common.size,
2N/A meta_number_to_string(msp->common.size, DEV_BSIZE)) == EOF)
2N/A return (mdsyserror(ep, errno, fname));
2N/A
2N/A /* print component details */
2N/A if (! metaismeta(msp->compnamep)) {
2N/A diskaddr_t start_blk;
2N/A int has_mddb;
2N/A char *has_mddb_str;
2N/A
2N/A /* print header */
2N/A /*
2N/A * Building a format string on the fly that will
2N/A * be used in (f)printf. This allows the length
2N/A * of the ctd to vary from small to large without
2N/A * looking horrible.
2N/A */
2N/A len = strlen(msp->compnamep->cname);
2N/A len = max(len, strlen(dgettext(TEXT_DOMAIN, "Device")));
2N/A len += 2;
2N/A if (fprintf(fp,
2N/A "\t%-*.*s %-12.12s %-5.5s %s\n",
2N/A len, len,
2N/A dgettext(TEXT_DOMAIN, "Device"),
2N/A dgettext(TEXT_DOMAIN, "Start Block"),
2N/A dgettext(TEXT_DOMAIN, "Dbase"),
2N/A dgettext(TEXT_DOMAIN, "Reloc")) == EOF) {
2N/A return (mdsyserror(ep, errno, fname));
2N/A }
2N/A
2N/A
2N/A /* get info */
2N/A if ((start_blk = meta_sp_get_start(sp, msp->compnamep, ep)) ==
2N/A MD_DISKADDR_ERROR)
2N/A return (-1);
2N/A
2N/A if ((has_mddb = metahasmddb(sp, msp->compnamep, ep)) < 0)
2N/A return (-1);
2N/A
2N/A if (has_mddb)
2N/A has_mddb_str = dgettext(TEXT_DOMAIN, "Yes");
2N/A else
2N/A has_mddb_str = dgettext(TEXT_DOMAIN, "No");
2N/A
2N/A /* populate the key in the name_p structure */
2N/A didnp = metadevname(&sp, msp->compnamep->dev, ep);
2N/A if (didnp == NULL) {
2N/A return (-1);
2N/A }
2N/A
2N/A /* determine if devid does NOT exist */
2N/A if (options & PRINT_DEVID) {
2N/A if ((dtp = meta_getdidbykey(sp->setno,
2N/A getmyside(sp, ep), didnp->key, ep)) == NULL)
2N/A devid = dgettext(TEXT_DOMAIN, "No ");
2N/A else {
2N/A devid = dgettext(TEXT_DOMAIN, "Yes");
2N/A free(dtp);
2N/A }
2N/A }
2N/A
2N/A /* print info */
2N/A /*
2N/A * This allows the length
2N/A * of the ctd to vary from small to large without
2N/A * looking horrible.
2N/A */
2N/A if (fprintf(fp, "\t%-*s %8lld %-5.5s %s\n",
2N/A len, msp->compnamep->cname,
2N/A start_blk, has_mddb_str, devid) == EOF) {
2N/A return (mdsyserror(ep, errno, fname));
2N/A }
2N/A (void) fprintf(fp, "\n");
2N/A }
2N/A
2N/A
2N/A /* print the headers */
2N/A if (fprintf(fp, "\t%6.6s %24.24s %24.24s\n",
2N/A dgettext(TEXT_DOMAIN, "Extent"),
2N/A dgettext(TEXT_DOMAIN, "Start Block"),
2N/A dgettext(TEXT_DOMAIN, "Block count")) == EOF)
2N/A return (mdsyserror(ep, errno, fname));
2N/A
2N/A /* print out each extent */
2N/A for (extn = 0; (extn < msp->ext.ext_len); extn++) {
2N/A md_sp_ext_t *extp = &msp->ext.ext_val[extn];
2N/A
2N/A /* If PRINT_TIMES option is ever supported, add output here */
2N/A if (fprintf(fp, "\t%6u %24llu %24llu\n",
2N/A extn, extp->poff, extp->len) == EOF)
2N/A return (mdsyserror(ep, errno, fname));
2N/A }
2N/A
2N/A /* separate records with a newline */
2N/A (void) fprintf(fp, "\n");
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * FUNCTION: meta_sp_print()
2N/A * INPUT: sp - the set name for the unit being displayed
2N/A * np - the name of the device to print
2N/A * fname - ??? not used
2N/A * fp - the file pointer to send output to
2N/A * options - print options from the command line processor
2N/A * OUTPUT: ep - return error pointer
2N/A * RETURNS: int - -1 if error, 0 on success
2N/A * PURPOSE: print a full report of the device specified by metastat.
2N/A * This is the main entry point for printing.
2N/A */
2N/Aint
2N/Ameta_sp_print(
2N/A mdsetname_t *sp,
2N/A mdname_t *np,
2N/A mdnamelist_t **nlpp,
2N/A char *fname,
2N/A FILE *fp,
2N/A mdprtopts_t options,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A md_sp_t *msp;
2N/A md_unit_t *mdp;
2N/A int rval = 0;
2N/A set_t setno;
2N/A minor_t unit;
2N/A
2N/A /* should always have the same set */
2N/A assert(sp != NULL);
2N/A
2N/A /* print all the soft partitions */
2N/A if (np == NULL) {
2N/A mdnamelist_t *nlp = NULL;
2N/A mdnamelist_t *p;
2N/A int cnt;
2N/A
2N/A if ((cnt = meta_get_sp_names(sp, &nlp, options, ep)) < 0)
2N/A return (-1);
2N/A else if (cnt == 0)
2N/A return (0);
2N/A
2N/A /* recusively print them out */
2N/A for (p = nlp; (p != NULL); p = p->next) {
2N/A mdname_t *curnp = p->namep;
2N/A
2N/A /*
2N/A * one problem with the rval of -1 here is that
2N/A * the error gets "lost" when the next device is
2N/A * printed, but we want to print them all anyway.
2N/A */
2N/A rval = meta_sp_print(sp, curnp, nlpp, fname, fp,
2N/A options, ep);
2N/A }
2N/A
2N/A /* clean up, return success */
2N/A metafreenamelist(nlp);
2N/A return (rval);
2N/A }
2N/A
2N/A /* get the unit structure */
2N/A if ((msp = meta_get_sp_common(sp, np,
2N/A ((options & PRINT_FAST) ? 1 : 0), ep)) == NULL)
2N/A return (-1);
2N/A
2N/A /* check for parented */
2N/A if ((! (options & PRINT_SUBDEVS)) &&
2N/A (MD_HAS_PARENT(msp->common.parent))) {
2N/A return (0);
2N/A }
2N/A
2N/A /* print appropriate detail */
2N/A if (options & PRINT_SHORT) {
2N/A if (meta_sp_short_print(msp, fname, fp, options, ep) != 0)
2N/A return (-1);
2N/A } else {
2N/A if (meta_sp_report(sp, msp, nlpp, fname, fp, options, ep) != 0)
2N/A return (-1);
2N/A }
2N/A
2N/A /*
2N/A * Print underlying metadevices if they are parented to us and
2N/A * if the info for the underlying metadevice has not been printed.
2N/A */
2N/A if (metaismeta(msp->compnamep)) {
2N/A /* get the unit structure for the subdevice */
2N/A if ((mdp = meta_get_mdunit(sp, msp->compnamep, ep)) == NULL)
2N/A return (-1);
2N/A
2N/A setno = MD_MIN2SET(MD_SID(mdp));
2N/A unit = MD_MIN2UNIT(MD_SID(mdp));
2N/A
2N/A /* If info not already printed, recurse */
2N/A if (sp_parent_printed[setno] == NULL ||
2N/A !BT_TEST(sp_parent_printed[setno], unit)) {
2N/A if (meta_print_name(sp, msp->compnamep, nlpp, fname, fp,
2N/A (options | PRINT_HEADER | PRINT_SUBDEVS),
2N/A NULL, ep) != 0) {
2N/A return (-1);
2N/A }
2N/A if (sp_parent_printed[setno] == NULL)
2N/A sp_parent_printed[setno] =
2N/A Zalloc(BT_SIZEOFMAP(MD_MAXUNITS));
2N/A BT_SET(sp_parent_printed[setno], unit);
2N/A }
2N/A }
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * **************************************************************************
2N/A * Watermark Manipulation Functions *
2N/A * **************************************************************************
2N/A */
2N/A
2N/A/*
2N/A * FUNCTION: meta_sp_get_start()
2N/A * INPUT: sp - the operating set
2N/A * np - device upon which the sp is being built
2N/A * OUTPUT: ep - return error pointer
2N/A * RETURNS: daddr_t - -1 if error, otherwise the start block
2N/A * PURPOSE: Encapsulate the determination of the start block of the
2N/A * device upon which the sp is built or being built.
2N/A */
2N/Astatic diskaddr_t
2N/Ameta_sp_get_start(
2N/A mdsetname_t *sp,
2N/A mdname_t *np,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A daddr_t start_block;
2N/A
2N/A if ((start_block = metagetstart(sp, np, ep)) != MD_DISKADDR_ERROR)
2N/A start_block += MD_SP_START;
2N/A
2N/A return (start_block);
2N/A}
2N/A
2N/A/*
2N/A * FUNCTION: meta_sp_update_wm_common()
2N/A * INPUT: sp - the operating set
2N/A * msp - a pointer to the XDR unit structure
2N/A * extlist - the extent list specifying watermarks to update
2N/A * iocval - either MD_IOC_SPUPDATEWM or MD_MN_IOC_SPUPDATEWM
2N/A * OUTPUT: ep - return error pointer
2N/A * RETURNS: int - -1 if error, 0 on success
2N/A * PURPOSE: steps backwards through the extent list updating
2N/A * watermarks for all extents with the EXTFLG_UPDATE flag
2N/A * set. Writing the watermarks guarantees consistency when
2N/A * extents must be broken into pieces since the original
2N/A * watermark will be the last to be updated, and will be
2N/A * changed to point to a new watermark that is already
2N/A * known to be consistent. If one of the writes fails, the
2N/A * original watermark stays intact and none of the changes
2N/A * are realized.
2N/A */
2N/Astatic int
2N/Ameta_sp_update_wm_common(
2N/A mdsetname_t *sp,
2N/A md_sp_t *msp,
2N/A sp_ext_node_t *extlist,
2N/A int iocval,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A sp_ext_node_t *ext;
2N/A sp_ext_node_t *tail;
2N/A mp_watermark_t *wmp, *watermarks;
2N/A xsp_offset_t *osp, *offsets;
2N/A int update_count = 0;
2N/A int rval = 0;
2N/A md_unit_t *mdp;
2N/A md_sp_update_wm_t update_params;
2N/A
2N/A if (getenv(META_SP_DEBUG)) {
2N/A meta_sp_debug("meta_sp_update_wm: Updating watermarks:\n");
2N/A meta_sp_list_dump(extlist);
2N/A }
2N/A
2N/A /*
2N/A * find the last node so we can write the watermarks backwards
2N/A * and count watermarks to update so we can allocate space
2N/A */
2N/A for (ext = extlist; ext != NULL; ext = ext->ext_next) {
2N/A if ((ext->ext_flags & EXTFLG_UPDATE) != 0) {
2N/A update_count++;
2N/A }
2N/A
2N/A if (ext->ext_next == NULL) {
2N/A tail = ext;
2N/A }
2N/A }
2N/A ext = tail;
2N/A
2N/A wmp = watermarks =
2N/A Zalloc(update_count * sizeof (mp_watermark_t));
2N/A osp = offsets =
2N/A Zalloc(update_count * sizeof (sp_ext_offset_t));
2N/A
2N/A while (ext != NULL) {
2N/A if ((ext->ext_flags & EXTFLG_UPDATE) != 0) {
2N/A /* update watermark */
2N/A wmp->wm_magic = MD_SP_MAGIC;
2N/A wmp->wm_version = MD_SP_VERSION;
2N/A wmp->wm_type = ext->ext_type;
2N/A wmp->wm_seq = ext->ext_seq;
2N/A wmp->wm_length = ext->ext_length - MD_SP_WMSIZE;
2N/A
2N/A /* fill in the volume name and set name */
2N/A if (ext->ext_namep != NULL)
2N/A (void) strcpy(wmp->wm_mdname,
2N/A ext->ext_namep->cname);
2N/A else
2N/A (void) strcpy(wmp->wm_mdname, MD_SP_FREEWMNAME);
2N/A if (ext->ext_setp != NULL &&
2N/A ext->ext_setp->setno != MD_LOCAL_SET)
2N/A (void) strcpy(wmp->wm_setname,
2N/A ext->ext_setp->setname);
2N/A else
2N/A (void) strcpy(wmp->wm_setname,
2N/A MD_SP_LOCALSETNAME);
2N/A
2N/A /* Generate the checksum */
2N/A wmp->wm_checksum = 0;
2N/A crcgen((uchar_t *)wmp, (uint_t *)&wmp->wm_checksum,
2N/A sizeof (*wmp), NULL);
2N/A
2N/A /* record the extent offset */
2N/A *osp = ext->ext_offset;
2N/A
2N/A /* Advance the placeholders */
2N/A osp++; wmp++;
2N/A }
2N/A ext = ext->ext_prev;
2N/A }
2N/A
2N/A mdp = meta_get_mdunit(sp, msp->common.namep, ep);
2N/A if (mdp == NULL) {
2N/A rval = -1;
2N/A goto out;
2N/A }
2N/A
2N/A (void) memset(&update_params, 0, sizeof (update_params));
2N/A update_params.mnum = MD_SID(mdp);
2N/A update_params.count = update_count;
2N/A update_params.wmp = (uintptr_t)watermarks;
2N/A update_params.osp = (uintptr_t)offsets;
2N/A MD_SETDRIVERNAME(&update_params, MD_SP,
2N/A MD_MIN2SET(update_params.mnum));
2N/A
2N/A if (metaioctl(iocval, &update_params, &update_params.mde,
2N/A msp->common.namep->cname) != 0) {
2N/A (void) mdstealerror(ep, &update_params.mde);
2N/A rval = -1;
2N/A goto out;
2N/A }
2N/A
2N/Aout:
2N/A Free(watermarks);
2N/A Free(offsets);
2N/A
2N/A return (rval);
2N/A}
2N/A
2N/Astatic int
2N/Ameta_sp_update_wm(
2N/A mdsetname_t *sp,
2N/A md_sp_t *msp,
2N/A sp_ext_node_t *extlist,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A return (meta_sp_update_wm_common(sp, msp, extlist, MD_IOC_SPUPDATEWM,
2N/A ep));
2N/A}
2N/A
2N/Astatic int
2N/Ameta_mn_sp_update_wm(
2N/A mdsetname_t *sp,
2N/A md_sp_t *msp,
2N/A sp_ext_node_t *extlist,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A return (meta_sp_update_wm_common(sp, msp, extlist, MD_MN_IOC_SPUPDATEWM,
2N/A ep));
2N/A}
2N/A
2N/A/*
2N/A * FUNCTION: meta_sp_clear_wm()
2N/A * INPUT: sp - the operating set
2N/A * msp - the unit structure for the soft partition to clear
2N/A * OUTPUT: ep - return error pointer
2N/A * RETURNS: int - -1 if error, 0 on success
2N/A * PURPOSE: steps through the extents for a soft partition unit and
2N/A * creates an extent list designed to mark all of the
2N/A * watermarks for those extents as free. The extent list
2N/A * is then passed to meta_sp_update_wm() to actually write
2N/A * the watermarks out.
2N/A */
2N/Astatic int
2N/Ameta_sp_clear_wm(
2N/A mdsetname_t *sp,
2N/A md_sp_t *msp,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A sp_ext_node_t *extlist = NULL;
2N/A int numexts = msp->ext.ext_len;
2N/A uint_t i;
2N/A int rval = 0;
2N/A
2N/A /* for each watermark must set the flag to SP_FREE */
2N/A for (i = 0; i < numexts; i++) {
2N/A md_sp_ext_t *extp = &msp->ext.ext_val[i];
2N/A
2N/A meta_sp_list_insert(NULL, NULL, &extlist,
2N/A extp->poff - MD_SP_WMSIZE, extp->len + MD_SP_WMSIZE,
2N/A EXTTYP_FREE, 0, EXTFLG_UPDATE, meta_sp_cmp_by_offset);
2N/A }
2N/A
2N/A /* update watermarks */
2N/A rval = meta_sp_update_wm(sp, msp, extlist, ep);
2N/A
2N/A meta_sp_list_free(&extlist);
2N/A return (rval);
2N/A}
2N/A
2N/A/*
2N/A * FUNCTION: meta_sp_read_wm()
2N/A * INPUT: sp - setname for component
2N/A * compnp - mdname_t for component
2N/A * offset - the offset of the watermark to read (sectors)
2N/A * OUTPUT: wm - the watermark structure to read into
2N/A * ep - return error pointer
2N/A * RETURNS: int - -1 if error, 0 on success
2N/A * PURPOSE: seeks out to the requested offset and reads a watermark.
2N/A * It then verifies that the magic number is correct and
2N/A * that the checksum is valid, returning an error if either
2N/A * is wrong.
2N/A */
2N/Astatic int
2N/Ameta_sp_read_wm(
2N/A mdsetname_t *sp,
2N/A mdname_t *compnp,
2N/A mp_watermark_t *wm,
2N/A sp_ext_offset_t offset,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A md_sp_read_wm_t read_params;
2N/A
2N/A /*
2N/A * make sure block offset does not overflow 2^64 bytes and it's a
2N/A * multiple of the block size.
2N/A */
2N/A assert(offset <= (1LL << (64 - DEV_BSHIFT)));
2N/A /* LINTED */
2N/A assert((sizeof (*wm) % DEV_BSIZE) == 0);
2N/A
2N/A (void) memset(wm, 0, sizeof (*wm));
2N/A
2N/A (void) memset(&read_params, 0, sizeof (read_params));
2N/A read_params.rdev = compnp->dev;
2N/A read_params.wmp = (uintptr_t)wm;
2N/A read_params.offset = offset;
2N/A MD_SETDRIVERNAME(&read_params, MD_SP, sp->setno);
2N/A
2N/A if (metaioctl(MD_IOC_SPREADWM, &read_params,
2N/A &read_params.mde, compnp->cname) != 0) {
2N/A
2N/A (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
2N/A "Extent header read failed, block %llu.\n"), offset);
2N/A return (mdstealerror(ep, &read_params.mde));
2N/A }
2N/A
2N/A /* make sure magic number is correct */
2N/A if (wm->wm_magic != MD_SP_MAGIC) {
2N/A (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
2N/A "found incorrect magic number %x, expected %x.\n"),
2N/A wm->wm_magic, MD_SP_MAGIC);
2N/A /*
2N/A * Pass NULL for the device name as we don't have
2N/A * valid watermark contents.
2N/A */
2N/A return (mdmderror(ep, MDE_SP_BADWMMAGIC, 0, NULL));
2N/A }
2N/A
2N/A if (crcchk((uchar_t *)wm, (uint_t *)&wm->wm_checksum,
2N/A sizeof (*wm), NULL)) {
2N/A (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
2N/A "found incorrect checksum %x.\n"),
2N/A wm->wm_checksum);
2N/A return (mdmderror(ep, MDE_SP_BADWMCRC, 0, wm->wm_mdname));
2N/A }
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * **************************************************************************
2N/A * Query Functions
2N/A * **************************************************************************
2N/A */
2N/A
2N/A/*
2N/A * IMPORTANT NOTE: This is a static function that assumes that
2N/A * its input parameters have been checked and
2N/A * have valid values that lie within acceptable
2N/A * ranges.
2N/A *
2N/A * FUNCTION: meta_sp_enough_space()
2N/A * INPUT: desired_number_of_sps - the number of soft partitions desired;
2N/A * must be > 0
2N/A * desired_sp_size - the desired soft partition size in blocks;
2N/A * must be > 0
2N/A * extent_listpp - a reference to a reference to an extent
2N/A * list that lists the extents on a device;
2N/A * must be a reference to a reference to a
2N/A * valid extent list
2N/A * alignment - the desired data space alignment for the sp's
2N/A * OUTPUT: boolean_t return value
2N/A * RETURNS: boolean_t - B_TRUE if there's enough space in the extent
2N/A * list to create the desired soft partitions,
2N/A * B_FALSE if there's not enough space
2N/A * PURPOSE: determines whether there's enough free space in an extent
2N/A * list to allow creation of a set of soft partitions
2N/A */
2N/Astatic boolean_t
2N/Ameta_sp_enough_space(
2N/A int desired_number_of_sps,
2N/A blkcnt_t desired_sp_size,
2N/A sp_ext_node_t **extent_listpp,
2N/A sp_ext_length_t alignment
2N/A)
2N/A{
2N/A boolean_t enough_space;
2N/A int number_of_sps;
2N/A int number_of_extents_used;
2N/A sp_ext_length_t desired_ext_length = desired_sp_size;
2N/A
2N/A enough_space = B_TRUE;
2N/A number_of_sps = 0;
2N/A while ((enough_space == B_TRUE) &&
2N/A (number_of_sps < desired_number_of_sps)) {
2N/A /*
2N/A * Use the extent allocation algorithm implemented by
2N/A * meta_sp_alloc_by_len() to test whether the free
2N/A * extents in the extent list referenced by *extent_listpp
2N/A * contain enough space to accomodate a soft partition
2N/A * of size desired_ext_length.
2N/A *
2N/A * Repeat the test <desired_number_of_sps> times
2N/A * or until it fails, whichever comes first,
2N/A * each time allocating the extents required to
2N/A * create the soft partition without actually
2N/A * creating the soft partition.
2N/A */
2N/A number_of_extents_used = meta_sp_alloc_by_len(
2N/A TEST_SETNAMEP, TEST_SOFT_PARTITION_NAMEP,
2N/A extent_listpp, &desired_ext_length,
2N/A NO_OFFSET, alignment);
2N/A if (number_of_extents_used == -1) {
2N/A enough_space = B_FALSE;
2N/A } else {
2N/A number_of_sps++;
2N/A }
2N/A }
2N/A return (enough_space);
2N/A}
2N/A
2N/A/*
2N/A * IMPORTANT NOTE: This is a static function that calls other functions
2N/A * that check its mdsetnamep and device_mdnamep
2N/A * input parameters, but expects extent_listpp to
2N/A * be a initialized to a valid address to which
2N/A * it can write a reference to the extent list that
2N/A * it creates.
2N/A *
2N/A * FUNCTION: meta_sp_get_extent_list()
2N/A * INPUT: mdsetnamep - a reference to the mdsetname_t structure
2N/A * for the set containing the device for
2N/A * which the extents are to be listed
2N/A * device_mdnamep - a reference to the mdname_t structure
2N/A * for the device for which the extents
2N/A * are to be listed
2N/A * OUTPUT: *extent_listpp - a reference to the extent list for
2N/A * the device; NULL if the function fails
2N/A * *ep - the libmeta error encountered, if any
2N/A * RETURNS: boolean_t - B_TRUE if the function call was successful,
2N/A * B_FALSE if not
2N/A * PURPOSE: gets the extent list for a device
2N/A */
2N/Astatic boolean_t
2N/Ameta_sp_get_extent_list(
2N/A mdsetname_t *mdsetnamep,
2N/A mdname_t *device_mdnamep,
2N/A sp_ext_node_t **extent_listpp,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A diskaddr_t device_size_in_blocks;
2N/A mdnamelist_t *sp_name_listp;
2N/A diskaddr_t start_block_address_in_blocks;
2N/A
2N/A *extent_listpp = NULL;
2N/A sp_name_listp = NULL;
2N/A
2N/A start_block_address_in_blocks = meta_sp_get_start(mdsetnamep,
2N/A device_mdnamep, ep);
2N/A if (start_block_address_in_blocks == MD_DISKADDR_ERROR) {
2N/A if (getenv(META_SP_DEBUG)) {
2N/A mde_perror(ep,
2N/A "meta_sp_get_extent_list:meta_sp_get_start");
2N/A }
2N/A return (B_FALSE);
2N/A }
2N/A
2N/A device_size_in_blocks = metagetsize(device_mdnamep, ep);
2N/A if (device_size_in_blocks == MD_DISKADDR_ERROR) {
2N/A if (getenv(META_SP_DEBUG)) {
2N/A mde_perror(ep,
2N/A "meta_sp_get_extent_list:metagetsize");
2N/A }
2N/A return (B_FALSE);
2N/A }
2N/A
2N/A /*
2N/A * Sanity check: the start block will have skipped an integer
2N/A * number of cylinders, C. C will usually be zero. If (C > 0),
2N/A * and the disk slice happens to only be C cylinders in total
2N/A * size, we'll fail this check.
2N/A */
2N/A if (device_size_in_blocks <=
2N/A (start_block_address_in_blocks + MD_SP_WMSIZE)) {
2N/A (void) mdmderror(ep, MDE_SP_NOSPACE, 0, device_mdnamep->cname);
2N/A return (B_FALSE);
2N/A }
2N/A
2N/A /*
2N/A * After this point, we will have allocated resources, so any
2N/A * failure returns must be through the supplied "fail" label
2N/A * to properly deallocate things.
2N/A */
2N/A
2N/A /*
2N/A * Create an empty extent list that starts one watermark past
2N/A * the start block of the device and ends one watermark before
2N/A * the end of the device.
2N/A */
2N/A meta_sp_list_insert(TEST_SETNAMEP, TEST_SOFT_PARTITION_NAMEP,
2N/A extent_listpp, NO_OFFSET,
2N/A (sp_ext_length_t)start_block_address_in_blocks,
2N/A EXTTYP_RESERVED, NO_SEQUENCE_NUMBER, NO_FLAGS,
2N/A meta_sp_cmp_by_offset);
2N/A meta_sp_list_insert(TEST_SETNAMEP, TEST_SOFT_PARTITION_NAMEP,
2N/A extent_listpp, (sp_ext_offset_t)(device_size_in_blocks -
2N/A MD_SP_WMSIZE), MD_SP_WMSIZE, EXTTYP_END, NO_SEQUENCE_NUMBER,
2N/A NO_FLAGS, meta_sp_cmp_by_offset);
2N/A
2N/A /*
2N/A * Get the list of soft partitions that are already on the
2N/A * device.
2N/A */
2N/A if (meta_sp_get_by_component(mdsetnamep, device_mdnamep,
2N/A &sp_name_listp, FORCE_RELOAD_CACHE, ep) < 1) {
2N/A if (getenv(META_SP_DEBUG)) {
2N/A mde_perror(ep,
2N/A "meta_sp_get_extent_list:meta_sp_get_by_component");
2N/A }
2N/A goto fail;
2N/A }
2N/A
2N/A if (sp_name_listp != NULL) {
2N/A /*
2N/A * If there are soft partitions on the device, add the
2N/A * extents used in them to the extent list.
2N/A */
2N/A if (meta_sp_extlist_from_namelist(mdsetnamep, sp_name_listp,
2N/A extent_listpp, ep) == -1) {
2N/A if (getenv(META_SP_DEBUG)) {
2N/A mde_perror(ep, "meta_sp_get_extent_list:"
2N/A "meta_sp_extlist_from_namelist");
2N/A }
2N/A goto fail;
2N/A }
2N/A metafreenamelist(sp_name_listp);
2N/A }
2N/A
2N/A /*
2N/A * Add free extents to the extent list to represent
2N/A * the remaining regions of free space on the
2N/A * device.
2N/A */
2N/A meta_sp_list_freefill(extent_listpp, device_size_in_blocks);
2N/A return (B_TRUE);
2N/A
2N/Afail:
2N/A if (sp_name_listp != NULL) {
2N/A metafreenamelist(sp_name_listp);
2N/A }
2N/A
2N/A if (*extent_listpp != NULL) {
2N/A /*
2N/A * meta_sp_list_free sets *extent_listpp to NULL.
2N/A */
2N/A meta_sp_list_free(extent_listpp);
2N/A }
2N/A return (B_FALSE);
2N/A}
2N/A
2N/A/*
2N/A * IMPORTANT NOTE: This is a static function that calls other functions
2N/A * that check its mdsetnamep and mddrivenamep
2N/A * input parameters, but expects extent_listpp to
2N/A * be a initialized to a valid address to which
2N/A * it can write a reference to the extent list that
2N/A * it creates.
2N/A *
2N/A * FUNCTION: meta_sp_get_extent_list_for_drive()
2N/A * INPUT: mdsetnamep - a reference to the mdsetname_t structure
2N/A * for the set containing the drive for
2N/A * which the extents are to be listed
2N/A * mddrivenamep - a reference to the mddrivename_t structure
2N/A * for the drive for which the extents
2N/A * are to be listed
2N/A * OUTPUT: *extent_listpp - a reference to the extent list for
2N/A * the drive; NULL if the function fails
2N/A * RETURNS: boolean_t - B_TRUE if the function call was successful,
2N/A * B_FALSE if not
2N/A * PURPOSE: gets the extent list for a drive when the entire drive
2N/A * is to be soft partitioned
2N/A */
2N/Astatic boolean_t
2N/Ameta_sp_get_extent_list_for_drive(
2N/A mdsetname_t *mdsetnamep,
2N/A mddrivename_t *mddrivenamep,
2N/A sp_ext_node_t **extent_listpp
2N/A)
2N/A{
2N/A boolean_t can_use;
2N/A diskaddr_t free_space;
2N/A md_error_t mderror;
2N/A mdvtoc_t proposed_vtoc;
2N/A int repartition_options;
2N/A int return_value;
2N/A md_sp_t test_sp_struct;
2N/A
2N/A can_use = B_TRUE;
2N/A *extent_listpp = NULL;
2N/A mderror = mdnullerror;
2N/A test_sp_struct.compnamep = metaslicename(mddrivenamep, MD_SLICE0,
2N/A &mderror);
2N/A if (test_sp_struct.compnamep == NULL) {
2N/A can_use = B_FALSE;
2N/A }
2N/A
2N/A if (can_use == B_TRUE) {
2N/A mderror = mdnullerror;
2N/A repartition_options = 0;
2N/A return_value = meta_check_sp(mdsetnamep, &test_sp_struct,
2N/A MDCMD_USE_WHOLE_DISK, &repartition_options, &mderror);
2N/A if (return_value != 0) {
2N/A can_use = B_FALSE;
2N/A }
2N/A }
2N/A
2N/A if (can_use == B_TRUE) {
2N/A mderror = mdnullerror;
2N/A repartition_options = repartition_options |
2N/A (MD_REPART_FORCE | MD_REPART_DONT_LABEL);
2N/A return_value = meta_repartition_drive(mdsetnamep, mddrivenamep,
2N/A repartition_options, &proposed_vtoc, &mderror);
2N/A if (return_value != 0) {
2N/A can_use = B_FALSE;
2N/A }
2N/A }
2N/A
2N/A if (can_use == B_TRUE) {
2N/A free_space = proposed_vtoc.parts[MD_SLICE0].size;
2N/A if (free_space <= (MD_SP_START + MD_SP_WMSIZE)) {
2N/A can_use = B_FALSE;
2N/A }
2N/A }
2N/A
2N/A if (can_use == B_TRUE) {
2N/A /*
2N/A * Create an extent list that starts with
2N/A * a reserved extent that ends at the start
2N/A * of the usable space on slice zero of the
2N/A * proposed VTOC, ends with an extent that
2N/A * reserves space for a watermark at the end
2N/A * of slice zero, and contains a single free
2N/A * extent that occupies the rest of the space
2N/A * on the slice.
2N/A *
2N/A * NOTE:
2N/A *
2N/A * Don't use metagetstart() or metagetsize() to
2N/A * find the usable space. They query the mdname_t
2N/A * structure that represents an actual device to
2N/A * determine the amount of space on the device that
2N/A * contains metadata and the total amount of space
2N/A * on the device. Since this function creates a
2N/A * proposed extent list that doesn't reflect the
2N/A * state of an actual device, there's no mdname_t
2N/A * structure to be queried.
2N/A *
2N/A * When a drive is reformatted to prepare for
2N/A * soft partitioning, all of slice seven is
2N/A * reserved for metadata, all of slice zero is
2N/A * available for soft partitioning, and all other
2N/A * slices on the drive are empty. The proposed
2N/A * extent list for the drive therefore contains
2N/A * only three extents: a reserved extent that ends
2N/A * at the start of the usable space on slice zero,
2N/A * a single free extent that occupies all the usable
2N/A * space on slice zero, and an ending extent that
2N/A * reserves space for a watermark at the end of
2N/A * slice zero.
2N/A */
2N/A meta_sp_list_insert(TEST_SETNAMEP, TEST_SOFT_PARTITION_NAMEP,
2N/A extent_listpp, NO_OFFSET, (sp_ext_length_t)(MD_SP_START),
2N/A EXTTYP_RESERVED, NO_SEQUENCE_NUMBER, NO_FLAGS,
2N/A meta_sp_cmp_by_offset);
2N/A meta_sp_list_insert(TEST_SETNAMEP, TEST_SOFT_PARTITION_NAMEP,
2N/A extent_listpp, (sp_ext_offset_t)(free_space - MD_SP_WMSIZE),
2N/A MD_SP_WMSIZE, EXTTYP_END, NO_SEQUENCE_NUMBER, NO_FLAGS,
2N/A meta_sp_cmp_by_offset);
2N/A meta_sp_list_freefill(extent_listpp, free_space);
2N/A }
2N/A return (can_use);
2N/A}
2N/A
2N/A/*
2N/A * FUNCTION: meta_sp_can_create_sps()
2N/A * INPUT: mdsetnamep - a reference to the mdsetname_t structure
2N/A * for the set containing the device for
2N/A * which the extents are to be listed
2N/A * mdnamep - a reference to the mdname_t of the device
2N/A * on which the soft parititions are to be created
2N/A * number_of_sps - the desired number of soft partitions
2N/A * sp_size - the desired soft partition size
2N/A * OUTPUT: boolean_t return value
2N/A * RETURNS: boolean_t - B_TRUE if the soft partitionns can be created,
2N/A * B_FALSE if not
2N/A * PURPOSE: determines whether a set of soft partitions can be created
2N/A * on a device
2N/A */
2N/Aboolean_t
2N/Ameta_sp_can_create_sps(
2N/A mdsetname_t *mdsetnamep,
2N/A mdname_t *mdnamep,
2N/A int number_of_sps,
2N/A blkcnt_t sp_size
2N/A)
2N/A{
2N/A sp_ext_node_t *extent_listp;
2N/A boolean_t succeeded;
2N/A md_error_t mde;
2N/A
2N/A if ((number_of_sps > 0) && (sp_size > 0)) {
2N/A succeeded = meta_sp_get_extent_list(mdsetnamep, mdnamep,
2N/A &extent_listp, &mde);
2N/A } else {
2N/A succeeded = B_FALSE;
2N/A }
2N/A
2N/A /*
2N/A * We don't really care about an error return from the
2N/A * alignment call; that will just result in passing zero,
2N/A * which will be interpreted as no alignment.
2N/A */
2N/A
2N/A if (succeeded == B_TRUE) {
2N/A succeeded = meta_sp_enough_space(number_of_sps,
2N/A sp_size, &extent_listp,
2N/A meta_sp_get_default_alignment(mdsetnamep, mdnamep, &mde));
2N/A meta_sp_list_free(&extent_listp);
2N/A }
2N/A return (succeeded);
2N/A}
2N/A
2N/A/*
2N/A * FUNCTION: meta_sp_can_create_sps_on_drive()
2N/A * INPUT: mdsetnamep - a reference to the mdsetname_t structure
2N/A * for the set containing the drive for
2N/A * which the extents are to be listed
2N/A * mddrivenamep - a reference to the mddrivename_t of the drive
2N/A * on which the soft parititions are to be created
2N/A * number_of_sps - the desired number of soft partitions
2N/A * sp_size - the desired soft partition size
2N/A * OUTPUT: boolean_t return value
2N/A * RETURNS: boolean_t - B_TRUE if the soft partitionns can be created,
2N/A * B_FALSE if not
2N/A * PURPOSE: determines whether a set of soft partitions can be created
2N/A * on a drive if the entire drive is soft partitioned
2N/A */
2N/Aboolean_t
2N/Ameta_sp_can_create_sps_on_drive(
2N/A mdsetname_t *mdsetnamep,
2N/A mddrivename_t *mddrivenamep,
2N/A int number_of_sps,
2N/A blkcnt_t sp_size
2N/A)
2N/A{
2N/A sp_ext_node_t *extent_listp;
2N/A boolean_t succeeded;
2N/A
2N/A if ((number_of_sps > 0) && (sp_size > 0)) {
2N/A succeeded = meta_sp_get_extent_list_for_drive(mdsetnamep,
2N/A mddrivenamep, &extent_listp);
2N/A } else {
2N/A succeeded = B_FALSE;
2N/A }
2N/A
2N/A /*
2N/A * We don't care about alignment on the space call because
2N/A * we're specifically dealing with a drive, which will have no
2N/A * inherent alignment.
2N/A */
2N/A
2N/A if (succeeded == B_TRUE) {
2N/A succeeded = meta_sp_enough_space(number_of_sps, sp_size,
2N/A &extent_listp, SP_UNALIGNED);
2N/A meta_sp_list_free(&extent_listp);
2N/A }
2N/A return (succeeded);
2N/A}
2N/A
2N/A/*
2N/A * FUNCTION: meta_sp_get_free_space()
2N/A * INPUT: mdsetnamep - a reference to the mdsetname_t structure
2N/A * for the set containing the device for
2N/A * which the free space is to be returned
2N/A * mdnamep - a reference to the mdname_t of the device
2N/A * for which the free space is to be returned
2N/A * OUTPUT: blkcnt_t return value
2N/A * RETURNS: blkcnt_t - the number of blocks of free space on the device
2N/A * PURPOSE: returns the number of blocks of free space on a device
2N/A */
2N/Ablkcnt_t
2N/Ameta_sp_get_free_space(
2N/A mdsetname_t *mdsetnamep,
2N/A mdname_t *mdnamep
2N/A)
2N/A{
2N/A sp_ext_node_t *extent_listp;
2N/A sp_ext_length_t free_blocks;
2N/A boolean_t succeeded;
2N/A md_error_t mde;
2N/A
2N/A extent_listp = NULL;
2N/A free_blocks = 0;
2N/A succeeded = meta_sp_get_extent_list(mdsetnamep, mdnamep,
2N/A &extent_listp, &mde);
2N/A if (succeeded == B_TRUE) {
2N/A free_blocks = meta_sp_list_size(extent_listp,
2N/A EXTTYP_FREE, INCLUDE_WM);
2N/A meta_sp_list_free(&extent_listp);
2N/A if (free_blocks > (10 * MD_SP_WMSIZE)) {
2N/A /*
2N/A * Subtract a safety margin for watermarks when
2N/A * computing the number of blocks available for
2N/A * use. The actual number of watermarks can't
2N/A * be calculated without knowing the exact numbers
2N/A * and sizes of both the free extents and the soft
2N/A * partitions to be created. The calculation is
2N/A * highly complex and error-prone even if those
2N/A * quantities are known. The approximate value
2N/A * 10 * MD_SP_WMSIZE is within a few blocks of the
2N/A * correct value in all practical cases.
2N/A */
2N/A free_blocks = free_blocks - (10 * MD_SP_WMSIZE);
2N/A } else {
2N/A free_blocks = 0;
2N/A }
2N/A } else {
2N/A mdclrerror(&mde);
2N/A }
2N/A
2N/A return (free_blocks);
2N/A}
2N/A
2N/A/*
2N/A * FUNCTION: meta_sp_get_free_space_on_drive()
2N/A * INPUT: mdsetnamep - a reference to the mdsetname_t structure
2N/A * for the set containing the drive for
2N/A * which the free space is to be returned
2N/A * mddrivenamep - a reference to the mddrivename_t of the drive
2N/A * for which the free space is to be returned
2N/A * OUTPUT: blkcnt_t return value
2N/A * RETURNS: blkcnt_t - the number of blocks of free space on the drive
2N/A * PURPOSE: returns the number of blocks of space usable for soft
2N/A * partitions on an entire drive, if the entire drive is
2N/A * soft partitioned
2N/A */
2N/Ablkcnt_t
2N/Ameta_sp_get_free_space_on_drive(
2N/A mdsetname_t *mdsetnamep,
2N/A mddrivename_t *mddrivenamep
2N/A)
2N/A{
2N/A sp_ext_node_t *extent_listp;
2N/A sp_ext_length_t free_blocks;
2N/A boolean_t succeeded;
2N/A
2N/A extent_listp = NULL;
2N/A free_blocks = 0;
2N/A succeeded = meta_sp_get_extent_list_for_drive(mdsetnamep,
2N/A mddrivenamep, &extent_listp);
2N/A if (succeeded == B_TRUE) {
2N/A free_blocks = meta_sp_list_size(extent_listp,
2N/A EXTTYP_FREE, INCLUDE_WM);
2N/A meta_sp_list_free(&extent_listp);
2N/A if (free_blocks > (10 * MD_SP_WMSIZE)) {
2N/A /*
2N/A * Subtract a safety margin for watermarks when
2N/A * computing the number of blocks available for
2N/A * use. The actual number of watermarks can't
2N/A * be calculated without knowing the exact numbers
2N/A * and sizes of both the free extents and the soft
2N/A * partitions to be created. The calculation is
2N/A * highly complex and error-prone even if those
2N/A * quantities are known. The approximate value
2N/A * 10 * MD_SP_WMSIZE is within a few blocks of the
2N/A * correct value in all practical cases.
2N/A */
2N/A free_blocks = free_blocks - (10 * MD_SP_WMSIZE);
2N/A } else {
2N/A free_blocks = 0;
2N/A }
2N/A }
2N/A return (free_blocks);
2N/A}
2N/A
2N/A/*
2N/A * FUNCTION: meta_sp_get_number_of_possible_sps()
2N/A * INPUT: mdsetnamep - a reference to the mdsetname_t structure
2N/A * for the set containing the device for
2N/A * which the number of possible soft partitions
2N/A * is to be returned
2N/A * mdnamep - a reference to the mdname_t of the device
2N/A * for which the number of possible soft partitions
2N/A * is to be returned
2N/A * OUTPUT: int return value
2N/A * RETURNS: int - the number of soft partitions of the desired size
2N/A * that can be created on the device
2N/A * PURPOSE: returns the number of soft partitions of a given size
2N/A * that can be created on a device
2N/A */
2N/Aint
2N/Ameta_sp_get_number_of_possible_sps(
2N/A mdsetname_t *mdsetnamep,
2N/A mdname_t *mdnamep,
2N/A blkcnt_t sp_size
2N/A)
2N/A{
2N/A sp_ext_node_t *extent_listp;
2N/A int number_of_possible_sps;
2N/A boolean_t succeeded;
2N/A md_error_t mde;
2N/A sp_ext_length_t alignment;
2N/A
2N/A extent_listp = NULL;
2N/A number_of_possible_sps = 0;
2N/A if (sp_size > 0) {
2N/A if ((succeeded = meta_sp_get_extent_list(mdsetnamep,
2N/A mdnamep, &extent_listp, &mde)) == B_FALSE)
2N/A mdclrerror(&mde);
2N/A } else {
2N/A succeeded = B_FALSE;
2N/A }
2N/A
2N/A if (succeeded == B_TRUE) {
2N/A alignment = meta_sp_get_default_alignment(mdsetnamep,
2N/A mdnamep, &mde);
2N/A }
2N/A
2N/A while (succeeded == B_TRUE) {
2N/A /*
2N/A * Keep allocating space from the extent list
2N/A * for soft partitions of the desired size until
2N/A * there's not enough free space left in the list
2N/A * for another soft partiition of that size.
2N/A * Add one to the number of possible soft partitions
2N/A * for each soft partition for which there is
2N/A * enough free space left.
2N/A */
2N/A succeeded = meta_sp_enough_space(ONE_SOFT_PARTITION,
2N/A sp_size, &extent_listp, alignment);
2N/A if (succeeded == B_TRUE) {
2N/A number_of_possible_sps++;
2N/A }
2N/A }
2N/A if (extent_listp != NULL) {
2N/A meta_sp_list_free(&extent_listp);
2N/A }
2N/A return (number_of_possible_sps);
2N/A}
2N/A
2N/A/*
2N/A * FUNCTION: meta_sp_get_number_of_possible_sps_on_drive()
2N/A * INPUT: mdsetnamep - a reference to the mdsetname_t structure
2N/A * for the set containing the drive for
2N/A * which the number of possible soft partitions
2N/A * is to be returned
2N/A * mddrivenamep - a reference to the mddrivename_t of the drive
2N/A * for which the number of possible soft partitions
2N/A * is to be returned
2N/A * sp_size - the size in blocks of the proposed soft partitions
2N/A * OUTPUT: int return value
2N/A * RETURNS: int - the number of soft partitions of the desired size
2N/A * that can be created on the drive
2N/A * PURPOSE: returns the number of soft partitions of a given size
2N/A * that can be created on a drive, if the entire drive is
2N/A * soft partitioned
2N/A */
2N/Aint
2N/Ameta_sp_get_number_of_possible_sps_on_drive(
2N/A mdsetname_t *mdsetnamep,
2N/A mddrivename_t *mddrivenamep,
2N/A blkcnt_t sp_size
2N/A)
2N/A{
2N/A sp_ext_node_t *extent_listp;
2N/A int number_of_possible_sps;
2N/A boolean_t succeeded;
2N/A
2N/A extent_listp = NULL;
2N/A number_of_possible_sps = 0;
2N/A if (sp_size > 0) {
2N/A succeeded = meta_sp_get_extent_list_for_drive(mdsetnamep,
2N/A mddrivenamep, &extent_listp);
2N/A } else {
2N/A succeeded = B_FALSE;
2N/A }
2N/A while (succeeded == B_TRUE) {
2N/A /*
2N/A * Keep allocating space from the extent list
2N/A * for soft partitions of the desired size until
2N/A * there's not enough free space left in the list
2N/A * for another soft partition of that size.
2N/A * Add one to the number of possible soft partitions
2N/A * for each soft partition for which there is
2N/A * enough free space left.
2N/A *
2N/A * Since it's a drive, not a metadevice, make no
2N/A * assumptions about alignment.
2N/A */
2N/A succeeded = meta_sp_enough_space(ONE_SOFT_PARTITION,
2N/A sp_size, &extent_listp, SP_UNALIGNED);
2N/A if (succeeded == B_TRUE) {
2N/A number_of_possible_sps++;
2N/A }
2N/A }
2N/A if (extent_listp != NULL) {
2N/A meta_sp_list_free(&extent_listp);
2N/A }
2N/A return (number_of_possible_sps);
2N/A}
2N/A
2N/A/*
2N/A * FUNCTION: meta_sp_get_possible_sp_size()
2N/A * INPUT: mdsetnamep - a reference to the mdsetname_t structure
2N/A * for the set containing the device for
2N/A * which the possible soft partition size
2N/A * is to be returned
2N/A * mdnamep - a reference to the mdname_t of the device
2N/A * for which the possible soft partition size
2N/A * is to be returned
2N/A * number_of_sps - the desired number of soft partitions
2N/A * OUTPUT: blkcnt_t return value
2N/A * RETURNS: blkcnt_t - the possible soft partition size in blocks
2N/A * PURPOSE: returns the maximum possible size of each of a given number of
2N/A * soft partitions of equal size that can be created on a device
2N/A */
2N/Ablkcnt_t
2N/Ameta_sp_get_possible_sp_size(
2N/A mdsetname_t *mdsetnamep,
2N/A mdname_t *mdnamep,
2N/A int number_of_sps
2N/A)
2N/A{
2N/A blkcnt_t free_blocks;
2N/A blkcnt_t sp_size;
2N/A boolean_t succeeded;
2N/A
2N/A sp_size = 0;
2N/A if (number_of_sps > 0) {
2N/A free_blocks = meta_sp_get_free_space(mdsetnamep, mdnamep);
2N/A sp_size = free_blocks / number_of_sps;
2N/A succeeded = meta_sp_can_create_sps(mdsetnamep, mdnamep,
2N/A number_of_sps, sp_size);
2N/A while ((succeeded == B_FALSE) && (sp_size > 0)) {
2N/A /*
2N/A * To compensate for space that may have been
2N/A * occupied by watermarks, reduce sp_size by a
2N/A * number of blocks equal to the number of soft
2N/A * partitions desired, and test again to see
2N/A * whether the desired number of soft partitions
2N/A * can be created.
2N/A */
2N/A sp_size = sp_size - ((blkcnt_t)number_of_sps);
2N/A succeeded = meta_sp_can_create_sps(mdsetnamep, mdnamep,
2N/A number_of_sps, sp_size);
2N/A }
2N/A if (sp_size < 0) {
2N/A sp_size = 0;
2N/A }
2N/A }
2N/A return (sp_size);
2N/A}
2N/A
2N/A/*
2N/A * FUNCTION: meta_sp_get_possible_sp_size_on_drive()
2N/A * INPUT: mdsetnamep - a reference to the mdsetname_t structure
2N/A * for the set containing the drive for
2N/A * which the possible soft partition size
2N/A * is to be returned
2N/A * mddrivenamep - a reference to the mddrivename_t of the drive
2N/A * for which the possible soft partition size
2N/A * is to be returned
2N/A * number_of_sps - the desired number of soft partitions
2N/A * OUTPUT: blkcnt_t return value
2N/A * RETURNS: blkcnt_t - the possible soft partition size in blocks
2N/A * PURPOSE: returns the maximum possible size of each of a given number of
2N/A * soft partitions of equal size that can be created on a drive
2N/A * if the entire drive is soft partitioned
2N/A */
2N/Ablkcnt_t
2N/Ameta_sp_get_possible_sp_size_on_drive(
2N/A mdsetname_t *mdsetnamep,
2N/A mddrivename_t *mddrivenamep,
2N/A int number_of_sps
2N/A)
2N/A{
2N/A blkcnt_t free_blocks;
2N/A blkcnt_t sp_size;
2N/A boolean_t succeeded;
2N/A
2N/A sp_size = 0;
2N/A if (number_of_sps > 0) {
2N/A free_blocks = meta_sp_get_free_space_on_drive(mdsetnamep,
2N/A mddrivenamep);
2N/A sp_size = free_blocks / number_of_sps;
2N/A succeeded = meta_sp_can_create_sps_on_drive(mdsetnamep,
2N/A mddrivenamep, number_of_sps, sp_size);
2N/A while ((succeeded == B_FALSE) && (sp_size > 0)) {
2N/A /*
2N/A * To compensate for space that may have been
2N/A * occupied by watermarks, reduce sp_size by a
2N/A * number of blocks equal to the number of soft
2N/A * partitions desired, and test again to see
2N/A * whether the desired number of soft partitions
2N/A * can be created.
2N/A */
2N/A sp_size = sp_size - ((blkcnt_t)number_of_sps);
2N/A succeeded = meta_sp_can_create_sps_on_drive(mdsetnamep,
2N/A mddrivenamep, number_of_sps, sp_size);
2N/A }
2N/A if (sp_size < 0) {
2N/A sp_size = 0;
2N/A }
2N/A }
2N/A return (sp_size);
2N/A}
2N/A
2N/A/*
2N/A * **************************************************************************
2N/A * Unit Structure Manipulation Functions *
2N/A * **************************************************************************
2N/A */
2N/A
2N/A/*
2N/A * FUNCTION: meta_sp_fillextarray()
2N/A * INPUT: mp - the unit structure to fill
2N/A * extlist - the list of extents to fill with
2N/A * OUTPUT: none
2N/A * RETURNS: void
2N/A * PURPOSE: fills in the unit structure extent list with the extents
2N/A * specified by extlist. Only extents in extlist with the
2N/A * EXTFLG_UPDATE flag are changed in the unit structure,
2N/A * and the index into the unit structure is the sequence
2N/A * number in the extent list. After all of the nodes have
2N/A * been updated the virtual offsets in the unit structure
2N/A * are updated to reflect the new lengths.
2N/A */
2N/Astatic void
2N/Ameta_sp_fillextarray(
2N/A mp_unit_t *mp,
2N/A sp_ext_node_t *extlist
2N/A)
2N/A{
2N/A int i;
2N/A sp_ext_node_t *ext;
2N/A sp_ext_offset_t curvoff = 0LL;
2N/A
2N/A assert(mp != NULL);
2N/A
2N/A /* go through the allocation list and fill in our unit structure */
2N/A for (ext = extlist; ext != NULL; ext = ext->ext_next) {
2N/A if ((ext->ext_type == EXTTYP_ALLOC) &&
2N/A (ext->ext_flags & EXTFLG_UPDATE) != 0) {
2N/A mp->un_ext[ext->ext_seq].un_poff =
2N/A ext->ext_offset + MD_SP_WMSIZE;
2N/A mp->un_ext[ext->ext_seq].un_len =
2N/A ext->ext_length - MD_SP_WMSIZE;
2N/A }
2N/A }
2N/A
2N/A for (i = 0; i < mp->un_numexts; i++) {
2N/A assert(mp->un_ext[i].un_poff != 0);
2N/A assert(mp->un_ext[i].un_len != 0);
2N/A mp->un_ext[i].un_voff = curvoff;
2N/A curvoff += mp->un_ext[i].un_len;
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * FUNCTION: meta_sp_createunit()
2N/A * INPUT: np - the name of the device to create a unit structure for
2N/A * compnp - the name of the device the soft partition is on
2N/A * extlist - the extent list to populate the new unit with
2N/A * numexts - the number of extents in the extent list
2N/A * len - the total size of the soft partition (sectors)
2N/A * status - the initial status of the unit structure
2N/A * OUTPUT: ep - return error pointer
2N/A * RETURNS: mp_unit_t * - the new unit structure.
2N/A * PURPOSE: allocates and fills in a new soft partition unit
2N/A * structure to be passed to the soft partitioning driver
2N/A * for creation.
2N/A */
2N/Astatic mp_unit_t *
2N/Ameta_sp_createunit(
2N/A mdname_t *np,
2N/A mdname_t *compnp,
2N/A sp_ext_node_t *extlist,
2N/A int numexts,
2N/A sp_ext_length_t len,
2N/A sp_status_t status,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A mp_unit_t *mp;
2N/A uint_t ms_size;
2N/A
2N/A ms_size = (sizeof (*mp) - sizeof (mp->un_ext[0])) +
2N/A (numexts * sizeof (mp->un_ext[0]));
2N/A
2N/A mp = Zalloc(ms_size);
2N/A
2N/A /* fill in fields in common unit structure */
2N/A mp->c.un_type = MD_METASP;
2N/A mp->c.un_size = ms_size;
2N/A MD_SID(mp) = meta_getminor(np->dev);
2N/A mp->c.un_total_blocks = len;
2N/A mp->c.un_actual_tb = len;
2N/A
2N/A /* set up geometry */
2N/A (void) meta_sp_setgeom(np, compnp, mp, ep);
2N/A
2N/A /* if we're building on metadevice we can't parent */
2N/A if (metaismeta(compnp))
2N/A MD_CAPAB(mp) = MD_CANT_PARENT;
2N/A else
2N/A MD_CAPAB(mp) = MD_CAN_PARENT;
2N/A
2N/A /* fill soft partition-specific fields */
2N/A mp->un_dev = compnp->dev;
2N/A mp->un_key = compnp->key;
2N/A
2N/A /* mdname_t start_blk field is not 64-bit! */
2N/A mp->un_start_blk = (sp_ext_offset_t)compnp->start_blk;
2N/A mp->un_status = status;
2N/A mp->un_numexts = numexts;
2N/A mp->un_length = len;
2N/A
2N/A /* fill in the extent array */
2N/A meta_sp_fillextarray(mp, extlist);
2N/A
2N/A return (mp);
2N/A}
2N/A
2N/A/*
2N/A * FUNCTION: meta_sp_updateunit()
2N/A * INPUT: np - name structure for the metadevice being updated
2N/A * old_un - the original unit structure that is being updated
2N/A * extlist - the extent list to populate the new unit with
2N/A * grow_len - the amount by which the partition is being grown
2N/A * numexts - the number of extents in the extent list
2N/A * ep - return error pointer
2N/A * OUTPUT: none
2N/A * RETURNS: mp_unit_t * - the updated unit structure
2N/A * PURPOSE: allocates and fills in a new soft partition unit structure to
2N/A * be passed to the soft partitioning driver for creation. The
2N/A * old unit structure is first copied in, and then the updated
2N/A * extents are changed in the new unit structure. This is
2N/A * typically used when the size of an existing unit is changed.
2N/A */
2N/Astatic mp_unit_t *
2N/Ameta_sp_updateunit(
2N/A mdname_t *np,
2N/A mp_unit_t *old_un,
2N/A sp_ext_node_t *extlist,
2N/A sp_ext_length_t grow_len,
2N/A int numexts,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A mp_unit_t *new_un;
2N/A sp_ext_length_t new_len;
2N/A uint_t new_size;
2N/A
2N/A assert(old_un != NULL);
2N/A assert(extlist != NULL);
2N/A
2N/A /* allocate new unit structure and copy in old unit */
2N/A new_size = (sizeof (*old_un) - sizeof (old_un->un_ext[0])) +
2N/A ((old_un->un_numexts + numexts) * sizeof (old_un->un_ext[0]));
2N/A new_len = old_un->un_length + grow_len;
2N/A new_un = Zalloc(new_size);
2N/A bcopy(old_un, new_un, old_un->c.un_size);
2N/A
2N/A /* update size and geometry information */
2N/A new_un->c.un_size = new_size;
2N/A new_un->un_length = new_len;
2N/A new_un->c.un_total_blocks = new_len;
2N/A new_un->c.un_actual_tb = new_len;
2N/A if (meta_adjust_geom((md_unit_t *)new_un, np,
2N/A old_un->c.un_wr_reinstruct, old_un->c.un_rd_reinstruct,
2N/A 0, ep) != 0) {
2N/A Free(new_un);
2N/A return (NULL);
2N/A }
2N/A
2N/A /* update extent information */
2N/A new_un->un_numexts += numexts;
2N/A
2N/A meta_sp_fillextarray(new_un, extlist);
2N/A
2N/A return (new_un);
2N/A}
2N/A
2N/A/*
2N/A * FUNCTION: meta_get_sp()
2N/A * INPUT: sp - the set name for the device to get
2N/A * np - the name of the device to get
2N/A * OUTPUT: ep - return error pointer
2N/A * RETURNS: md_sp_t * - the XDR unit structure for the soft partition
2N/A * PURPOSE: interface to the rest of libmeta for fetching a unit structure
2N/A * for the named device. Just a wrapper for meta_get_sp_common().
2N/A */
2N/Amd_sp_t *
2N/Ameta_get_sp(
2N/A mdsetname_t *sp,
2N/A mdname_t *np,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A return (meta_get_sp_common(sp, np, 0, ep));
2N/A}
2N/A
2N/A/*
2N/A * FUNCTION: meta_get_sp_common()
2N/A * INPUT: sp - the set name for the device to get
2N/A * np - the name of the device to get
2N/A * fast - whether to use the cache or not (NOT IMPLEMENTED!)
2N/A * OUTPUT: ep - return error pointer
2N/A * RETURNS: md_sp_t * - the XDR unit structure for the soft partition,
2N/A * NULL if np is not a soft partition
2N/A * PURPOSE: common routine for fetching a soft partition unit structure
2N/A */
2N/Amd_sp_t *
2N/Ameta_get_sp_common(
2N/A mdsetname_t *sp,
2N/A mdname_t *np,
2N/A int fast,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A mddrivename_t *dnp = np->drivenamep;
2N/A char *miscname;
2N/A mp_unit_t *mp;
2N/A md_sp_t *msp;
2N/A int i;
2N/A
2N/A /* must have set */
2N/A assert(sp != NULL);
2N/A
2N/A /* short circuit */
2N/A if (dnp->unitp != NULL) {
2N/A if (dnp->unitp->type != MD_METASP)
2N/A return (NULL);
2N/A return ((md_sp_t *)dnp->unitp);
2N/A }
2N/A /* get miscname and unit */
2N/A if ((miscname = metagetmiscname(np, ep)) == NULL)
2N/A return (NULL);
2N/A
2N/A if (strcmp(miscname, MD_SP) != 0) {
2N/A (void) mdmderror(ep, MDE_NOT_SP, 0, np->cname);
2N/A return (NULL);
2N/A }
2N/A
2N/A if ((mp = (mp_unit_t *)meta_get_mdunit(sp, np, ep)) == NULL)
2N/A return (NULL);
2N/A
2N/A assert(mp->c.un_type == MD_METASP);
2N/A
2N/A /* allocate soft partition */
2N/A msp = Zalloc(sizeof (*msp));
2N/A
2N/A /* get the common information */
2N/A msp->common.namep = np;
2N/A msp->common.type = mp->c.un_type;
2N/A msp->common.state = mp->c.un_status;
2N/A msp->common.capabilities = mp->c.un_capabilities;
2N/A msp->common.parent = mp->c.un_parent;
2N/A msp->common.size = mp->c.un_total_blocks;
2N/A msp->common.user_flags = mp->c.un_user_flags;
2N/A msp->common.revision = mp->c.un_revision;
2N/A
2N/A /* get soft partition information */
2N/A if ((msp->compnamep = metakeyname(&sp, mp->un_key, fast, ep)) == NULL)
2N/A goto out;
2N/A
2N/A /*
2N/A * Fill in the key and the start block. Note that the start
2N/A * block in the unit structure is 64 bits but the name pointer
2N/A * only supports 32 bits.
2N/A */
2N/A msp->compnamep->key = mp->un_key;
2N/A msp->compnamep->start_blk = mp->un_start_blk;
2N/A
2N/A /* fill in status field */
2N/A msp->status = mp->un_status;
2N/A
2N/A /* allocate the extents */
2N/A msp->ext.ext_val = Zalloc(mp->un_numexts * sizeof (*msp->ext.ext_val));
2N/A msp->ext.ext_len = mp->un_numexts;
2N/A
2N/A /* do the extents for this soft partition */
2N/A for (i = 0; i < mp->un_numexts; i++) {
2N/A struct mp_ext *mde = &mp->un_ext[i];
2N/A md_sp_ext_t *extp = &msp->ext.ext_val[i];
2N/A
2N/A extp->voff = mde->un_voff;
2N/A extp->poff = mde->un_poff;
2N/A extp->len = mde->un_len;
2N/A }
2N/A
2N/A /* cleanup, return success */
2N/A Free(mp);
2N/A dnp->unitp = (md_common_t *)msp;
2N/A return (msp);
2N/A
2N/Aout:
2N/A /* clean up and return error */
2N/A Free(mp);
2N/A Free(msp);
2N/A return (NULL);
2N/A}
2N/A
2N/A
2N/A/*
2N/A * FUNCTION: meta_init_sp()
2N/A * INPUT: spp - the set name for the new device
2N/A * argc - the remaining argument count for the metainit cmdline
2N/A * argv - the remainder of the unparsed command line
2N/A * options - global options parsed by metainit
2N/A * OUTPUT: ep - return error pointer
2N/A * RETURNS: int - -1 failure, 0 success
2N/A * PURPOSE: provides the command line parsing and name management overhead
2N/A * for creating a new soft partition. Ultimately this calls
2N/A * meta_create_sp() which does the real work of allocating space
2N/A * for the new soft partition.
2N/A */
2N/Aint
2N/Ameta_init_sp(
2N/A mdsetname_t **spp,
2N/A int argc,
2N/A char *argv[],
2N/A mdcmdopts_t options,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A char *compname = NULL;
2N/A mdname_t *spcompnp = NULL; /* name of component volume */
2N/A char *devname = argv[0]; /* unit name */
2N/A mdname_t *np = NULL; /* name of soft partition */
2N/A md_sp_t *msp = NULL;
2N/A int c;
2N/A int old_optind;
2N/A sp_ext_length_t len = 0LL;
2N/A int rval = -1;
2N/A uint_t seq;
2N/A int oflag;
2N/A int failed;
2N/A mddrivename_t *dnp = NULL;
2N/A sp_ext_length_t alignment = 0LL;
2N/A sp_ext_node_t *extlist = NULL;
2N/A
2N/A assert(argc > 0);
2N/A
2N/A /* expect sp name, -p, optional -e, compname, and size parameters */
2N/A /* grab soft partition name */
2N/A if ((np = metaname(spp, devname, META_DEVICE, ep)) == NULL)
2N/A goto out;
2N/A
2N/A /* see if it exists already */
2N/A if (metagetmiscname(np, ep) != NULL) {
2N/A (void) mdmderror(ep, MDE_UNIT_ALREADY_SETUP,
2N/A meta_getminor(np->dev), devname);
2N/A goto out;
2N/A } else if (! mdismderror(ep, MDE_UNIT_NOT_SETUP)) {
2N/A goto out;
2N/A } else {
2N/A mdclrerror(ep);
2N/A }
2N/A --argc, ++argv;
2N/A
2N/A if (argc == 0)
2N/A goto syntax;
2N/A
2N/A /* grab -p */
2N/A if (strcmp(argv[0], "-p") != 0)
2N/A goto syntax;
2N/A --argc, ++argv;
2N/A
2N/A if (argc == 0)
2N/A goto syntax;
2N/A
2N/A /* see if -e is there */
2N/A if (strcmp(argv[0], "-e") == 0) {
2N/A /* use the whole disk */
2N/A options |= MDCMD_USE_WHOLE_DISK;
2N/A --argc, ++argv;
2N/A }
2N/A
2N/A if (argc == 0)
2N/A goto syntax;
2N/A
2N/A /* get component name */
2N/A compname = Strdup(argv[0]);
2N/A
2N/A if (options & MDCMD_USE_WHOLE_DISK) {
2N/A if ((dnp = metadrivename(spp, compname, ep)) == NULL) {
2N/A goto out;
2N/A }
2N/A if ((spcompnp = metaslicename(dnp, 0, ep)) == NULL) {
2N/A goto out;
2N/A }
2N/A } else if ((spcompnp = metaname(spp, compname, UNKNOWN, ep)) == NULL) {
2N/A goto out;
2N/A }
2N/A assert(*spp != NULL);
2N/A
2N/A if (!(options & MDCMD_NOLOCK)) {
2N/A /* grab set lock */
2N/A if (meta_lock(*spp, TRUE, ep))
2N/A goto out;
2N/A
2N/A if (meta_check_ownership(*spp, ep) != 0)
2N/A goto out;
2N/A }
2N/A
2N/A /* allocate the soft partition */
2N/A msp = Zalloc(sizeof (*msp));
2N/A
2N/A /* setup common */
2N/A msp->common.namep = np;
2N/A msp->common.type = MD_METASP;
2N/A
2N/A compname = spcompnp->cname;
2N/A
2N/A assert(spcompnp->rname != NULL);
2N/A --argc, ++argv;
2N/A
2N/A if (argc == 0) {
2N/A goto syntax;
2N/A }
2N/A
2N/A if (*argv[0] == '-') {
2N/A /*
2N/A * parse any other command line options, this includes
2N/A * the recovery options -o and -b. The special thing
2N/A * with these options is that the len needs to be
2N/A * kept track of otherwise when the geometry of the
2N/A * "device" is built it will create an invalid geometry
2N/A */
2N/A old_optind = optind = 0;
2N/A opterr = 0;
2N/A oflag = 0;
2N/A seq = 0;
2N/A failed = 0;
2N/A while ((c = getopt(argc, argv, "A:o:b:")) != -1) {
2N/A sp_ext_offset_t offset;
2N/A sp_ext_length_t length;
2N/A longlong_t tmp_size;
2N/A
2N/A switch (c) {
2N/A case 'A': /* data alignment */
2N/A if (meta_sp_parsesizestring(optarg,
2N/A &alignment) == -1) {
2N/A failed = 1;
2N/A }
2N/A break;
2N/A case 'o': /* offset in the partition */
2N/A if (oflag == 1) {
2N/A failed = 1;
2N/A } else {
2N/A tmp_size = atoll(optarg);
2N/A if (tmp_size <= 0) {
2N/A failed = 1;
2N/A } else {
2N/A oflag = 1;
2N/A options |= MDCMD_DIRECT;
2N/A
2N/A offset = tmp_size;
2N/A }
2N/A }
2N/A
2N/A break;
2N/A case 'b': /* number of blocks */
2N/A if (oflag == 0) {
2N/A failed = 1;
2N/A } else {
2N/A tmp_size = atoll(optarg);
2N/A if (tmp_size <= 0) {
2N/A failed = 1;
2N/A } else {
2N/A oflag = 0;
2N/A
2N/A length = tmp_size;
2N/A
2N/A /* we have a pair of values */
2N/A meta_sp_list_insert(*spp, np,
2N/A &extlist, offset, length,
2N/A EXTTYP_ALLOC, seq++,
2N/A EXTFLG_UPDATE,
2N/A meta_sp_cmp_by_offset);
2N/A len += length;
2N/A }
2N/A }
2N/A
2N/A break;
2N/A default:
2N/A argc -= old_optind;
2N/A argv += old_optind;
2N/A goto options;
2N/A }
2N/A
2N/A if (failed) {
2N/A argc -= old_optind;
2N/A argv += old_optind;
2N/A goto syntax;
2N/A }
2N/A
2N/A old_optind = optind;
2N/A }
2N/A argc -= optind;
2N/A argv += optind;
2N/A
2N/A /*
2N/A * Must have matching pairs of -o and -b flags
2N/A */
2N/A if (oflag != 0)
2N/A goto syntax;
2N/A
2N/A /*
2N/A * Can't specify both layout (indicated indirectly by
2N/A * len being set by thye -o/-b cases above) AND
2N/A * alignment
2N/A */
2N/A if ((len > 0LL) && (alignment > 0LL))
2N/A goto syntax;
2N/A
2N/A /*
2N/A * sanity check the allocation list
2N/A */
2N/A if ((extlist != NULL) && meta_sp_list_overlaps(extlist))
2N/A goto syntax;
2N/A }
2N/A
2N/A if (len == 0LL) {
2N/A if (argc == 0)
2N/A goto syntax;
2N/A if (meta_sp_parsesize(argv[0], &len) == -1)
2N/A goto syntax;
2N/A --argc, ++argv;
2N/A }
2N/A
2N/A msp->ext.ext_val = Zalloc(sizeof (*msp->ext.ext_val));
2N/A msp->ext.ext_val->len = len;
2N/A msp->compnamep = spcompnp;
2N/A
2N/A /* we should be at the end */
2N/A if (argc != 0)
2N/A goto syntax;
2N/A
2N/A /* create soft partition */
2N/A if (meta_create_sp(*spp, msp, extlist, options, alignment, ep) != 0)
2N/A goto out;
2N/A rval = 0;
2N/A
2N/A /* let em know */
2N/A if (options & MDCMD_PRINT) {
2N/A (void) printf(dgettext(TEXT_DOMAIN,
2N/A "%s: Soft Partition is setup\n"),
2N/A devname);
2N/A (void) fflush(stdout);
2N/A }
2N/A goto out;
2N/A
2N/Asyntax:
2N/A /* syntax error */
2N/A rval = meta_cook_syntax(ep, MDE_SYNTAX, compname, argc, argv);
2N/A goto out;
2N/A
2N/Aoptions:
2N/A /* options error */
2N/A rval = meta_cook_syntax(ep, MDE_OPTION, compname, argc, argv);
2N/A goto out;
2N/A
2N/Aout:
2N/A if (msp != NULL) {
2N/A if (msp->ext.ext_val != NULL) {
2N/A Free(msp->ext.ext_val);
2N/A }
2N/A Free(msp);
2N/A }
2N/A
2N/A return (rval);
2N/A}
2N/A
2N/A/*
2N/A * FUNCTION: meta_free_sp()
2N/A * INPUT: msp - the soft partition unit to free
2N/A * OUTPUT: none
2N/A * RETURNS: void
2N/A * PURPOSE: provides an interface from the rest of libmeta for freeing a
2N/A * soft partition unit
2N/A */
2N/Avoid
2N/Ameta_free_sp(md_sp_t *msp)
2N/A{
2N/A Free(msp);
2N/A}
2N/A
2N/A/*
2N/A * FUNCTION: meta_sp_issp()
2N/A * INPUT: sp - the set name to check
2N/A * np - the name to check
2N/A * OUTPUT: ep - return error pointer
2N/A * RETURNS: int - 0 means sp,np is a soft partition
2N/A * 1 means sp,np is not a soft partition
2N/A * PURPOSE: determines whether the given device is a soft partition
2N/A * device. This is called by other metadevice check routines.
2N/A */
2N/Aint
2N/Ameta_sp_issp(
2N/A mdsetname_t *sp,
2N/A mdname_t *np,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A if (meta_get_sp_common(sp, np, 0, ep) == NULL)
2N/A return (1);
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * FUNCTION: meta_check_sp()
2N/A * INPUT: sp - the set name to check
2N/A * msp - the unit structure to check
2N/A * options - creation options
2N/A * OUTPUT: repart_options - options to be passed to
2N/A * meta_repartition_drive()
2N/A * ep - return error pointer
2N/A * RETURNS: int - 0 ok to create on this component
2N/A * -1 error or not ok to create on this component
2N/A * PURPOSE: Checks to determine whether the rules for creation of
2N/A * soft partitions allow creation of a soft partition on
2N/A * the device described by the mdname_t structure referred
2N/A * to by msp->compnamep.
2N/A *
2N/A * NOTE: Does NOT check to determine whether the extents
2N/A * described in the md_sp_t structure referred to by
2N/A * msp will fit on the device described by the mdname_t
2N/A * structure located at msp->compnamep.
2N/A */
2N/Astatic int
2N/Ameta_check_sp(
2N/A mdsetname_t *sp,
2N/A md_sp_t *msp,
2N/A mdcmdopts_t options,
2N/A int *repart_options,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A md_common_t *mdp;
2N/A mdname_t *compnp = msp->compnamep;
2N/A uint_t slice;
2N/A mddrivename_t *dnp;
2N/A mdname_t *slicenp;
2N/A mdvtoc_t *vtocp;
2N/A
2N/A /* make sure it is in the set */
2N/A if (meta_check_inset(sp, compnp, ep) != 0)
2N/A return (-1);
2N/A
2N/A if ((options & MDCMD_USE_WHOLE_DISK) != 0) {
2N/A uint_t rep_slice;
2N/A
2N/A /*
2N/A * check to make sure we can partition this drive.
2N/A * we cannot continue if any of the following are
2N/A * true:
2N/A * The drive is a metadevice.
2N/A * The drive contains a mounted slice.
2N/A * The drive contains a slice being swapped to.
2N/A * The drive contains slices which are part of other
2N/A * metadevices.
2N/A * The drive contains a metadb.
2N/A */
2N/A if (metaismeta(compnp))
2N/A return (mddeverror(ep, MDE_IS_META, compnp->dev,
2N/A compnp->cname));
2N/A
2N/A assert(compnp->drivenamep != NULL);
2N/A
2N/A /*
2N/A * ensure that we have slice 0 since the disk will be
2N/A * repartitioned in the USE_WHOLE_DISK case. this check
2N/A * is redundant unless the user incorrectly specifies a
2N/A * a fully qualified drive AND slice name (i.e.,
2N/A * /dev/dsk/cXtXdXsX), which will be incorrectly
2N/A * recognized as a drive name by the metaname code.
2N/A */
2N/A
2N/A if ((vtocp = metagetvtoc(compnp, FALSE, &slice, ep)) == NULL)
2N/A return (-1);
2N/A if (slice != MD_SLICE0)
2N/A return (mderror(ep, MDE_NOT_DRIVENAME, compnp->cname));
2N/A
2N/A dnp = compnp->drivenamep;
2N/A if (meta_replicaslice(dnp, &rep_slice, ep) != 0)
2N/A return (-1);
2N/A
2N/A for (slice = 0; slice < vtocp->nparts; slice++) {
2N/A
2N/A /* only check if the slice really exists */
2N/A if (vtocp->parts[slice].size == 0)
2N/A continue;
2N/A
2N/A slicenp = metaslicename(dnp, slice, ep);
2N/A if (slicenp == NULL)
2N/A return (-1);
2N/A
2N/A /* check to ensure that it is not already in use */
2N/A if (meta_check_inuse(sp,
2N/A slicenp, MDCHK_INUSE, ep) != 0) {
2N/A return (-1);
2N/A }
2N/A
2N/A /*
2N/A * Up to this point, tests are applied to all
2N/A * slices uniformly.
2N/A */
2N/A
2N/A if (slice == rep_slice) {
2N/A /*
2N/A * Tests inside the body of this
2N/A * conditional are applied only to
2N/A * slice seven.
2N/A */
2N/A if (meta_check_inmeta(sp, slicenp,
2N/A options | MDCHK_ALLOW_MDDB |
2N/A MDCHK_ALLOW_REPSLICE, 0, -1, ep) != 0)
2N/A return (-1);
2N/A
2N/A /*
2N/A * For slice seven, a metadb is NOT an
2N/A * automatic failure. It merely means
2N/A * that we're not allowed to muck
2N/A * about with the partitioning of that
2N/A * slice. We indicate this by masking
2N/A * in the MD_REPART_LEAVE_REP flag.
2N/A */
2N/A if (metahasmddb(sp, slicenp, ep)) {
2N/A assert(repart_options !=
2N/A NULL);
2N/A *repart_options |=
2N/A MD_REPART_LEAVE_REP;
2N/A }
2N/A
2N/A /*
2N/A * Skip the remaining tests for slice
2N/A * seven
2N/A */
2N/A continue;
2N/A }
2N/A
2N/A /*
2N/A * Tests below this point will be applied to
2N/A * all slices EXCEPT for the replica slice.
2N/A */
2N/A
2N/A
2N/A /* check if component is in a metadevice */
2N/A if (meta_check_inmeta(sp, slicenp, options, 0,
2N/A -1, ep) != 0)
2N/A return (-1);
2N/A
2N/A /* check to see if component has a metadb */
2N/A if (metahasmddb(sp, slicenp, ep))
2N/A return (mddeverror(ep, MDE_HAS_MDDB,
2N/A slicenp->dev, slicenp->cname));
2N/A }
2N/A /*
2N/A * This should be all of the testing necessary when
2N/A * the MDCMD_USE_WHOLE_DISK flag is set; the rest of
2N/A * meta_check_sp() is oriented towards component
2N/A * arguments instead of disks.
2N/A */
2N/A goto meta_check_sp_ok;
2N/A
2N/A }
2N/A
2N/A /* check to ensure that it is not already in use */
2N/A if (meta_check_inuse(sp, compnp, MDCHK_INUSE, ep) != 0) {
2N/A return (-1);
2N/A }
2N/A
2N/A if (!metaismeta(compnp)) { /* handle non-metadevices */
2N/A
2N/A /*
2N/A * The component can have one or more soft partitions on it
2N/A * already, but can't be part of any other type of metadevice,
2N/A * so if it is used for a metadevice, but the metadevice
2N/A * isn't a soft partition, return failure.
2N/A */
2N/A
2N/A if (meta_check_inmeta(sp, compnp, options, 0, -1, ep) != 0 &&
2N/A meta_check_insp(sp, compnp, 0, -1, ep) == 0) {
2N/A return (-1);
2N/A }
2N/A } else { /* handle metadevices */
2N/A /* get underlying unit & check capabilities */
2N/A if ((mdp = meta_get_unit(sp, compnp, ep)) == NULL)
2N/A return (-1);
2N/A
2N/A if ((! (mdp->capabilities & MD_CAN_PARENT)) ||
2N/A (! (mdp->capabilities & MD_CAN_SP)))
2N/A return (mdmderror(ep, MDE_INVAL_UNIT,
2N/A meta_getminor(compnp->dev), compnp->cname));
2N/A }
2N/A
2N/Ameta_check_sp_ok:
2N/A mdclrerror(ep);
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * FUNCTION: meta_create_sp()
2N/A * INPUT: sp - the set name to create in
2N/A * msp - the unit structure to create
2N/A * oblist - an optional list of requested extents (-o/-b options)
2N/A * options - creation options
2N/A * alignment - data alignment
2N/A * OUTPUT: ep - return error pointer
2N/A * RETURNS: int - 0 success, -1 error
2N/A * PURPOSE: does most of the work for creating a soft partition. If
2N/A * metainit -p -e was used, first partition the drive. Then
2N/A * create an extent list based on the existing soft partitions
2N/A * and assume all space not used by them is free. Storage for
2N/A * the new soft partition is allocated from the free extents
2N/A * based on the length specified on the command line or the
2N/A * oblist passed in. The unit structure is then committed and
2N/A * the watermarks are updated. Finally, the status is changed to
2N/A * Okay and the process is complete.
2N/A */
2N/Astatic int
2N/Ameta_create_sp(
2N/A mdsetname_t *sp,
2N/A md_sp_t *msp,
2N/A sp_ext_node_t *oblist,
2N/A mdcmdopts_t options,
2N/A sp_ext_length_t alignment,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A mdname_t *np = msp->common.namep;
2N/A mdname_t *compnp = msp->compnamep;
2N/A mp_unit_t *mp = NULL;
2N/A mdnamelist_t *keynlp = NULL, *spnlp = NULL;
2N/A md_set_params_t set_params;
2N/A int rval = -1;
2N/A diskaddr_t comp_size;
2N/A diskaddr_t sp_start;
2N/A sp_ext_node_t *extlist = NULL;
2N/A int numexts = 0; /* number of extents */
2N/A int count = 0;
2N/A int committed = 0;
2N/A int repart_options = MD_REPART_FORCE;
2N/A int create_flag = MD_CRO_32BIT;
2N/A int mn_set_master = 0;
2N/A
2N/A md_set_desc *sd;
2N/A md_set_mmown_params_t *ownpar = NULL;
2N/A int comp_is_mirror = 0;
2N/A
2N/A /* validate soft partition */
2N/A if (meta_check_sp(sp, msp, options, &repart_options, ep) != 0)
2N/A return (-1);
2N/A
2N/A if ((options & MDCMD_USE_WHOLE_DISK) != 0) {
2N/A if ((options & MDCMD_DOIT) != 0) {
2N/A if (meta_repartition_drive(sp,
2N/A compnp->drivenamep,
2N/A repart_options,
2N/A NULL, /* Don't return the VTOC */
2N/A ep) != 0)
2N/A
2N/A return (-1);
2N/A } else {
2N/A /*
2N/A * If -n and -e are both specified, it doesn't make
2N/A * sense to continue without actually partitioning
2N/A * the drive.
2N/A */
2N/A return (0);
2N/A }
2N/A }
2N/A
2N/A /* populate the start_blk field of the component name */
2N/A if ((sp_start = meta_sp_get_start(sp, compnp, ep)) ==
2N/A MD_DISKADDR_ERROR) {
2N/A rval = -1;
2N/A goto out;
2N/A }
2N/A
2N/A if (options & MDCMD_DOIT) {
2N/A /* store name in namespace */
2N/A if (add_key_name(sp, compnp, &keynlp, ep) != 0) {
2N/A rval = -1;
2N/A goto out;
2N/A }
2N/A }
2N/A
2N/A /*
2N/A * Get a list of the soft partitions that currently reside on
2N/A * the component. We should ALWAYS force reload the cache,
2N/A * because if this is a single creation, there will not BE a
2N/A * cached list, and if we're using the md.tab, we must rebuild
2N/A * the list because it won't contain the previous (if any)
2N/A * soft partition.
2N/A */
2N/A count = meta_sp_get_by_component(sp, compnp, &spnlp, 1, ep);
2N/A if (count < 0) {
2N/A /* error occured */
2N/A rval = -1;
2N/A goto out;
2N/A }
2N/A
2N/A /*
2N/A * get the size of the underlying device. if the size is smaller
2N/A * than or equal to the watermark size, we know there isn't
2N/A * enough space.
2N/A */
2N/A if ((comp_size = metagetsize(compnp, ep)) == MD_DISKADDR_ERROR) {
2N/A rval = -1;
2N/A goto out;
2N/A } else if (comp_size <= MD_SP_WMSIZE) {
2N/A (void) mdmderror(ep, MDE_SP_NOSPACE, 0, compnp->cname);
2N/A rval = -1;
2N/A goto out;
2N/A }
2N/A /*
2N/A * seed extlist with reserved space at the beginning of the volume and
2N/A * enough space for the end watermark. The end watermark always gets
2N/A * updated, but if the underlying device changes size it may not be
2N/A * pointed to until the extent before it is updated. Since the
2N/A * end of the reserved space is where the first watermark starts,
2N/A * the reserved extent should never be marked for updating.
2N/A */
2N/A
2N/A meta_sp_list_insert(NULL, NULL, &extlist,
2N/A 0ULL, sp_start, EXTTYP_RESERVED, 0, 0, meta_sp_cmp_by_offset);
2N/A meta_sp_list_insert(NULL, NULL, &extlist,
2N/A (sp_ext_offset_t)(comp_size - MD_SP_WMSIZE), MD_SP_WMSIZE,
2N/A EXTTYP_END, 0, EXTFLG_UPDATE, meta_sp_cmp_by_offset);
2N/A
2N/A if (meta_sp_extlist_from_namelist(sp, spnlp, &extlist, ep) == -1) {
2N/A rval = -1;
2N/A goto out;
2N/A }
2N/A
2N/A metafreenamelist(spnlp);
2N/A
2N/A if (getenv(META_SP_DEBUG)) {
2N/A meta_sp_debug("meta_create_sp: list of used extents:\n");
2N/A meta_sp_list_dump(extlist);
2N/A }
2N/A
2N/A meta_sp_list_freefill(&extlist, metagetsize(compnp, ep));
2N/A
2N/A /* get extent list from -o/-b options or from free space */
2N/A if (options & MDCMD_DIRECT) {
2N/A if (getenv(META_SP_DEBUG)) {
2N/A meta_sp_debug("meta_create_sp: Dumping -o/-b list:\n");
2N/A meta_sp_list_dump(oblist);
2N/A }
2N/A
2N/A numexts = meta_sp_alloc_by_list(sp, np, &extlist, oblist);
2N/A if (numexts == -1) {
2N/A (void) mdmderror(ep, MDE_SP_OVERLAP, 0, np->cname);
2N/A rval = -1;
2N/A goto out;
2N/A }
2N/A } else {
2N/A numexts = meta_sp_alloc_by_len(sp, np, &extlist,
2N/A &msp->ext.ext_val->len, 0LL, (alignment > 0) ? alignment :
2N/A meta_sp_get_default_alignment(sp, compnp, ep));
2N/A if (numexts == -1) {
2N/A (void) mdmderror(ep, MDE_SP_NOSPACE, 0, np->cname);
2N/A rval = -1;
2N/A goto out;
2N/A }
2N/A }
2N/A
2N/A assert(extlist != NULL);
2N/A
2N/A /* create soft partition */
2N/A mp = meta_sp_createunit(msp->common.namep, msp->compnamep,
2N/A extlist, numexts, msp->ext.ext_val->len, MD_SP_CREATEPEND, ep);
2N/A
2N/A create_flag = meta_check_devicesize(mp->c.un_total_blocks);
2N/A
2N/A /* if we're not doing anything (metainit -n), return success */
2N/A if (! (options & MDCMD_DOIT)) {
2N/A rval = 0; /* success */
2N/A goto out;
2N/A }
2N/A
2N/A (void) memset(&set_params, 0, sizeof (set_params));
2N/A
2N/A if (create_flag == MD_CRO_64BIT) {
2N/A mp->c.un_revision |= MD_64BIT_META_DEV;
2N/A set_params.options = MD_CRO_64BIT;
2N/A } else {
2N/A mp->c.un_revision &= ~MD_64BIT_META_DEV;
2N/A set_params.options = MD_CRO_32BIT;
2N/A }
2N/A
2N/A if (getenv(META_SP_DEBUG)) {
2N/A meta_sp_debug("meta_create_sp: printing unit structure\n");
2N/A meta_sp_printunit(mp);
2N/A }
2N/A
2N/A /*
2N/A * Check to see if we're trying to create a partition on a mirror. If so
2N/A * we may have to enforce an ownership change before writing the
2N/A * watermark out.
2N/A */
2N/A if (metaismeta(compnp)) {
2N/A char *miscname;
2N/A
2N/A miscname = metagetmiscname(compnp, ep);
2N/A if (miscname != NULL)
2N/A comp_is_mirror = (strcmp(miscname, MD_MIRROR) == 0);
2N/A else
2N/A comp_is_mirror = 0;
2N/A } else {
2N/A comp_is_mirror = 0;
2N/A }
2N/A
2N/A /*
2N/A * For a multi-node environment we have to ensure that the master
2N/A * node owns an underlying mirror before we issue the MD_IOCSET ioctl.
2N/A * If the master does not own the device we will deadlock as the
2N/A * implicit write of the watermarks (in sp_ioctl.c) will cause an
2N/A * ownership change that will block as the MD_IOCSET is still in
2N/A * progress. To close this window we force an owner change to occur
2N/A * before issuing the MD_IOCSET. We cannot simply open the device and
2N/A * write to it as this will only work for the first soft-partition
2N/A * creation.
2N/A */
2N/A
2N/A if (comp_is_mirror && !metaislocalset(sp)) {
2N/A
2N/A if ((sd = metaget_setdesc(sp, ep)) == NULL) {
2N/A rval = -1;
2N/A goto out;
2N/A }
2N/A if (MD_MNSET_DESC(sd) && sd->sd_mn_am_i_master) {
2N/A mn_set_master = 1;
2N/A }
2N/A }
2N/A
2N/A set_params.mnum = MD_SID(mp);
2N/A set_params.size = mp->c.un_size;
2N/A set_params.mdp = (uintptr_t)mp;
2N/A MD_SETDRIVERNAME(&set_params, MD_SP, MD_MIN2SET(set_params.mnum));
2N/A
2N/A /* first phase of commit. */
2N/A if (metaioctl(MD_IOCSET, &set_params, &set_params.mde,
2N/A np->cname) != 0) {
2N/A (void) mdstealerror(ep, &set_params.mde);
2N/A rval = -1;
2N/A goto out;
2N/A }
2N/A
2N/A /* we've successfully committed the record */
2N/A committed = 1;
2N/A
2N/A /* write watermarks */
2N/A /*
2N/A * Special-case for Multi-node sets. As we now have a distributed DRL
2N/A * update mechanism, we _will_ hit the ioctl-within-ioctl deadlock case
2N/A * unless we use a 'special' MN-capable ioctl to stage the watermark
2N/A * update. This only affects the master-node in an MN set.
2N/A */
2N/A if (mn_set_master) {
2N/A if (meta_mn_sp_update_wm(sp, msp, extlist, ep) < 0) {
2N/A rval = -1;
2N/A goto out;
2N/A }
2N/A } else {
2N/A if (meta_sp_update_wm(sp, msp, extlist, ep) < 0) {
2N/A rval = -1;
2N/A goto out;
2N/A }
2N/A }
2N/A
2N/A /* second phase of commit, set status to MD_SP_OK */
2N/A if (meta_sp_setstatus(sp, &(MD_SID(mp)), 1, MD_SP_OK, ep) < 0) {
2N/A rval = -1;
2N/A goto out;
2N/A }
2N/A rval = 0;
2N/Aout:
2N/A Free(mp);
2N/A if (ownpar)
2N/A Free(ownpar);
2N/A
2N/A if (extlist != NULL)
2N/A meta_sp_list_free(&extlist);
2N/A
2N/A if (rval != 0 && keynlp != NULL && committed != 1)
2N/A (void) del_key_names(sp, keynlp, NULL);
2N/A
2N/A metafreenamelist(keynlp);
2N/A
2N/A return (rval);
2N/A}
2N/A
2N/A/*
2N/A * **************************************************************************
2N/A * Reset (metaclear) Functions *
2N/A * **************************************************************************
2N/A */
2N/A
2N/A/*
2N/A * FUNCTION: meta_sp_reset_common()
2N/A * INPUT: sp - the set name of the device to reset
2N/A * np - the name of the device to reset
2N/A * msp - the unit structure to reset
2N/A * options - metaclear options
2N/A * OUTPUT: ep - return error pointer
2N/A * RETURNS: int - 0 success, -1 error
2N/A * PURPOSE: "resets", or more accurately deletes, the soft partition
2N/A * specified. First the state is set to "deleting" and then the
2N/A * watermarks are all cleared out. Once the watermarks have been
2N/A * updated, the unit structure is deleted from the metadb.
2N/A */
2N/Astatic int
2N/Ameta_sp_reset_common(
2N/A mdsetname_t *sp,
2N/A mdname_t *np,
2N/A md_sp_t *msp,
2N/A md_sp_reset_t reset_params,
2N/A mdcmdopts_t options,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A char *miscname;
2N/A int rval = -1;
2N/A int is_open = 0;
2N/A
2N/A /* make sure that nobody owns us */
2N/A if (MD_HAS_PARENT(msp->common.parent))
2N/A return (mdmderror(ep, MDE_IN_USE, meta_getminor(np->dev),
2N/A np->cname));
2N/A
2N/A /* make sure that the soft partition isn't open */
2N/A if ((is_open = meta_isopen(sp, np, ep, options)) < 0)
2N/A return (-1);
2N/A else if (is_open)
2N/A return (mdmderror(ep, MDE_IS_OPEN, meta_getminor(np->dev),
2N/A np->cname));
2N/A
2N/A /* get miscname */
2N/A if ((miscname = metagetmiscname(np, ep)) == NULL)
2N/A return (-1);
2N/A
2N/A /* fill in reset params */
2N/A MD_SETDRIVERNAME(&reset_params, miscname, sp->setno);
2N/A reset_params.mnum = meta_getminor(np->dev);
2N/A reset_params.force = (options & MDCMD_FORCE) ? 1 : 0;
2N/A
2N/A /*
2N/A * clear soft partition - phase one.
2N/A * place the soft partition into the "delete pending" state.
2N/A */
2N/A if (meta_sp_setstatus(sp, &reset_params.mnum, 1, MD_SP_DELPEND, ep) < 0)
2N/A return (-1);
2N/A
2N/A /*
2N/A * Now clear the watermarks. If the force flag is specified,
2N/A * ignore any errors writing the watermarks and delete the unit
2N/A * structure anyway. An error may leave the on-disk format in a
2N/A * corrupt state. If force is not specified and we fail here,
2N/A * the soft partition will remain in the "delete pending" state.
2N/A */
2N/A if ((meta_sp_clear_wm(sp, msp, ep) < 0) &&
2N/A ((options & MDCMD_FORCE) == 0))
2N/A goto out;
2N/A
2N/A /*
2N/A * clear soft partition - phase two.
2N/A * the driver removes the soft partition from the metadb and
2N/A * zeros out incore version.
2N/A */
2N/A if (metaioctl(MD_IOCRESET, &reset_params,
2N/A &reset_params.mde, np->cname) != 0) {
2N/A (void) mdstealerror(ep, &reset_params.mde);
2N/A goto out;
2N/A }
2N/A
2N/A /*
2N/A * Wait for the /dev to be cleaned up. Ignore the return
2N/A * value since there's not much we can do.
2N/A */
2N/A (void) meta_update_devtree(meta_getminor(np->dev));
2N/A
2N/A rval = 0; /* success */
2N/A
2N/A if (options & MDCMD_PRINT) {
2N/A (void) printf(dgettext(TEXT_DOMAIN,
2N/A "%s: Soft Partition is cleared\n"),
2N/A np->cname);
2N/A (void) fflush(stdout);
2N/A }
2N/A
2N/A /*
2N/A * if told to recurse and on a metadevice, then attempt to
2N/A * clear the subdevices. Indicate failure if the clear fails.
2N/A */
2N/A if ((options & MDCMD_RECURSE) &&
2N/A (metaismeta(msp->compnamep)) &&
2N/A (meta_reset_by_name(sp, msp->compnamep, options, ep) != 0))
2N/A rval = -1;
2N/A
2N/Aout:
2N/A meta_invalidate_name(np);
2N/A return (rval);
2N/A}
2N/A
2N/A/*
2N/A * FUNCTION: meta_sp_reset()
2N/A * INPUT: sp - the set name of the device to reset
2N/A * np - the name of the device to reset
2N/A * options - metaclear options
2N/A * OUTPUT: ep - return error pointer
2N/A * RETURNS: int - 0 success, -1 error
2N/A * PURPOSE: provides the entry point to the rest of libmeta for deleting a
2N/A * soft partition. If np is NULL, then soft partitions are
2N/A * all deleted at the current level and then recursively deleted.
2N/A * Otherwise, if a name is specified either directly or as a
2N/A * result of a recursive operation, it deletes only that name.
2N/A * Since something sitting under a soft partition may be parented
2N/A * to it, we have to reparent that other device to another soft
2N/A * partition on the same component if we're deleting the one it's
2N/A * parented to.
2N/A */
2N/Aint
2N/Ameta_sp_reset(
2N/A mdsetname_t *sp,
2N/A mdname_t *np,
2N/A mdcmdopts_t options,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A md_sp_t *msp;
2N/A int rval = -1;
2N/A mdnamelist_t *spnlp = NULL, *nlp = NULL;
2N/A md_sp_reset_t reset_params;
2N/A int num_sp;
2N/A
2N/A assert(sp != NULL);
2N/A
2N/A /* reset/delete all soft paritions */
2N/A if (np == NULL) {
2N/A /*
2N/A * meta_reset_all sets MDCMD_RECURSE, but this behavior
2N/A * is incorrect for soft partitions. We want to clear
2N/A * all soft partitions at a particular level in the
2N/A * metadevice stack before moving to the next level.
2N/A * Thus, we clear MDCMD_RECURSE from the options.
2N/A */
2N/A options &= ~MDCMD_RECURSE;
2N/A
2N/A /* for each soft partition */
2N/A rval = 0;
2N/A if (meta_get_sp_names(sp, &spnlp, 0, ep) < 0)
2N/A rval = -1;
2N/A
2N/A for (nlp = spnlp; (nlp != NULL); nlp = nlp->next) {
2N/A np = nlp->namep;
2N/A if ((msp = meta_get_sp(sp, np, ep)) == NULL) {
2N/A rval = -1;
2N/A break;
2N/A }
2N/A /*
2N/A * meta_reset_all calls us twice to get soft
2N/A * partitions at the top and bottom of the stack.
2N/A * thus, if we have a parent, we'll get deleted
2N/A * on the next call.
2N/A */
2N/A if (MD_HAS_PARENT(msp->common.parent))
2N/A continue;
2N/A /*
2N/A * If this is a multi-node set, we send a series
2N/A * of individual metaclear commands.
2N/A */
2N/A if (meta_is_mn_set(sp, ep)) {
2N/A if (meta_mn_send_metaclear_command(sp,
2N/A np->cname, options, 0, ep) != 0) {
2N/A rval = -1;
2N/A break;
2N/A }
2N/A } else {
2N/A if (meta_sp_reset(sp, np, options, ep) != 0) {
2N/A rval = -1;
2N/A break;
2N/A }
2N/A }
2N/A }
2N/A /* cleanup return status */
2N/A metafreenamelist(spnlp);
2N/A return (rval);
2N/A }
2N/A
2N/A /* check the name */
2N/A if (metachkmeta(np, ep) != 0)
2N/A return (-1);
2N/A
2N/A /* get the unit structure */
2N/A if ((msp = meta_get_sp(sp, np, ep)) == NULL)
2N/A return (-1);
2N/A
2N/A /* clear out reset parameters */
2N/A (void) memset(&reset_params, 0, sizeof (reset_params));
2N/A
2N/A /* if our child is a metadevice, we need to deparent/reparent it */
2N/A if (metaismeta(msp->compnamep)) {
2N/A /* get sp's on this component */
2N/A if ((num_sp = meta_sp_get_by_component(sp, msp->compnamep,
2N/A &spnlp, 1, ep)) <= 0)
2N/A /* no sp's on this device. error! */
2N/A return (-1);
2N/A else if (num_sp == 1)
2N/A /* last sp on this device, so we deparent */
2N/A reset_params.new_parent = MD_NO_PARENT;
2N/A else {
2N/A /* have to reparent this metadevice */
2N/A for (nlp = spnlp; nlp != NULL; nlp = nlp->next) {
2N/A if (meta_getminor(nlp->namep->dev) ==
2N/A meta_getminor(np->dev))
2N/A continue;
2N/A /*
2N/A * this isn't the softpart we are deleting,
2N/A * so use this device as the new parent.
2N/A */
2N/A reset_params.new_parent =
2N/A meta_getminor(nlp->namep->dev);
2N/A break;
2N/A }
2N/A }
2N/A metafreenamelist(spnlp);
2N/A }
2N/A
2N/A if (meta_sp_reset_common(sp, np, msp, reset_params, options, ep) != 0)
2N/A return (-1);
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * FUNCTION: meta_sp_reset_component()
2N/A * INPUT: sp - the set name of the device to reset
2N/A * name - the string name of the device to reset
2N/A * options - metaclear options
2N/A * OUTPUT: ep - return error pointer
2N/A * RETURNS: int - 0 success, -1 error
2N/A * PURPOSE: provides the ability to delete all soft partitions on a
2N/A * specified device (metaclear -p). It first gets all of the
2N/A * soft partitions on the component and then deletes each one
2N/A * individually.
2N/A */
2N/Aint
2N/Ameta_sp_reset_component(
2N/A mdsetname_t *sp,
2N/A char *name,
2N/A mdcmdopts_t options,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A mdname_t *compnp, *np;
2N/A mdnamelist_t *spnlp = NULL;
2N/A mdnamelist_t *nlp = NULL;
2N/A md_sp_t *msp;
2N/A int count;
2N/A md_sp_reset_t reset_params;
2N/A
2N/A if ((compnp = metaname(&sp, name, UNKNOWN, ep)) == NULL)
2N/A return (-1);
2N/A
2N/A /* If we're starting out with no soft partitions, it's an error */
2N/A count = meta_sp_get_by_component(sp, compnp, &spnlp, 1, ep);
2N/A if (count == 0)
2N/A return (mdmderror(ep, MDE_SP_NOSP, 0, compnp->cname));
2N/A else if (count < 0)
2N/A return (-1);
2N/A
2N/A /*
2N/A * clear all soft partitions on this component.
2N/A * NOTE: we reparent underlying metadevices as we go so that
2N/A * things stay sane. Also, if we encounter an error, we stop
2N/A * and go no further in case recovery might be needed.
2N/A */
2N/A for (nlp = spnlp; nlp != NULL; nlp = nlp->next) {
2N/A /* clear out reset parameters */
2N/A (void) memset(&reset_params, 0, sizeof (reset_params));
2N/A
2N/A /* check the name */
2N/A np = nlp->namep;
2N/A
2N/A if (metachkmeta(np, ep) != 0) {
2N/A metafreenamelist(spnlp);
2N/A return (-1);
2N/A }
2N/A
2N/A /* get the unit structure */
2N/A if ((msp = meta_get_sp(sp, np, ep)) == NULL) {
2N/A metafreenamelist(spnlp);
2N/A return (-1);
2N/A }
2N/A
2N/A /* have to deparent/reparent metadevices */
2N/A if (metaismeta(compnp)) {
2N/A if (nlp->next == NULL)
2N/A reset_params.new_parent = MD_NO_PARENT;
2N/A else
2N/A reset_params.new_parent =
2N/A meta_getminor(spnlp->next->namep->dev);
2N/A }
2N/A
2N/A /* clear soft partition */
2N/A if (meta_sp_reset_common(sp, np, msp, reset_params,
2N/A options, ep) < 0) {
2N/A metafreenamelist(spnlp);
2N/A return (-1);
2N/A }
2N/A }
2N/A metafreenamelist(spnlp);
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * **************************************************************************
2N/A * Grow (metattach) Functions *
2N/A * **************************************************************************
2N/A */
2N/A
2N/A/*
2N/A * FUNCTION: meta_sp_attach()
2N/A * INPUT: sp - the set name of the device to attach to
2N/A * np - the name of the device to attach to
2N/A * addsize - the unparsed string holding the amount of space to add
2N/A * options - metattach options
2N/A * alignment - data alignment
2N/A * OUTPUT: ep - return error pointer
2N/A * RETURNS: int - 0 success, -1 error
2N/A * PURPOSE: grows a soft partition by reading in the existing unit
2N/A * structure and setting its state to Growing, allocating more
2N/A * space (similar to meta_create_sp()), updating the watermarks,
2N/A * and then writing out the new unit structure in the Okay state.
2N/A */
2N/Aint
2N/Ameta_sp_attach(
2N/A mdsetname_t *sp,
2N/A mdname_t *np,
2N/A char *addsize,
2N/A mdcmdopts_t options,
2N/A sp_ext_length_t alignment,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A md_grow_params_t grow_params;
2N/A sp_ext_length_t grow_len; /* amount to grow */
2N/A mp_unit_t *mp, *new_un;
2N/A mdname_t *compnp = NULL;
2N/A
2N/A sp_ext_node_t *extlist = NULL;
2N/A int numexts;
2N/A mdnamelist_t *spnlp = NULL;
2N/A int count;
2N/A md_sp_t *msp;
2N/A daddr_t start_block;
2N/A
2N/A /* should have the same set */
2N/A assert(sp != NULL);
2N/A assert(sp->setno == MD_MIN2SET(meta_getminor(np->dev)));
2N/A
2N/A /* check name */
2N/A if (metachkmeta(np, ep) != 0)
2N/A return (-1);
2N/A
2N/A if (meta_sp_parsesize(addsize, &grow_len) == -1) {
2N/A return (mdmderror(ep, MDE_SP_BAD_LENGTH, 0, np->cname));
2N/A }
2N/A
2N/A if ((mp = (mp_unit_t *)meta_get_mdunit(sp, np, ep)) == NULL)
2N/A return (-1);
2N/A
2N/A /* make sure we don't have a parent */
2N/A if (MD_HAS_PARENT(mp->c.un_parent)) {
2N/A Free(mp);
2N/A return (mdmderror(ep, MDE_INVAL_UNIT, 0, np->cname));
2N/A }
2N/A
2N/A if (getenv(META_SP_DEBUG)) {
2N/A meta_sp_debug("meta_sp_attach: Unit structure before new "
2N/A "space:\n");
2N/A meta_sp_printunit(mp);
2N/A }
2N/A
2N/A /*
2N/A * NOTE: the fast option to metakeyname is 0 as opposed to 1
2N/A * If this was not the case we would suffer the following
2N/A * assertion failure:
2N/A * Assertion failed: type1 != MDT_FAST_META && type1 != MDT_FAST_COMP
2N/A * file meta_check.x, line 315
2N/A * I guess this is because we have not "seen" this drive before
2N/A * and hence hit the failure - this is of course the attach routine
2N/A */
2N/A if ((compnp = metakeyname(&sp, mp->un_key, 0, ep)) == NULL) {
2N/A Free(mp);
2N/A return (-1);
2N/A }
2N/A
2N/A /* metakeyname does not fill in the key. */
2N/A compnp->key = mp->un_key;
2N/A
2N/A /* work out the space on the component that we are dealing with */
2N/A count = meta_sp_get_by_component(sp, compnp, &spnlp, 0, ep);
2N/A
2N/A /*
2N/A * see if the component has been soft partitioned yet, or if an
2N/A * error occurred.
2N/A */
2N/A if (count == 0) {
2N/A Free(mp);
2N/A return (mdmderror(ep, MDE_NOT_SP, 0, np->cname));
2N/A } else if (count < 0) {
2N/A Free(mp);
2N/A return (-1);
2N/A }
2N/A
2N/A /*
2N/A * seed extlist with reserved space at the beginning of the volume and
2N/A * enough space for the end watermark. The end watermark always gets
2N/A * updated, but if the underlying device changes size it may not be
2N/A * pointed to until the extent before it is updated. Since the
2N/A * end of the reserved space is where the first watermark starts,
2N/A * the reserved extent should never be marked for updating.
2N/A */
2N/A if ((start_block = meta_sp_get_start(sp, compnp, ep)) ==
2N/A MD_DISKADDR_ERROR) {
2N/A Free(mp);
2N/A return (-1);
2N/A }
2N/A
2N/A meta_sp_list_insert(NULL, NULL, &extlist, 0ULL, start_block,
2N/A EXTTYP_RESERVED, 0, 0, meta_sp_cmp_by_offset);
2N/A meta_sp_list_insert(NULL, NULL, &extlist,
2N/A metagetsize(compnp, ep) - MD_SP_WMSIZE, MD_SP_WMSIZE,
2N/A EXTTYP_END, 0, EXTFLG_UPDATE, meta_sp_cmp_by_offset);
2N/A
2N/A if (meta_sp_extlist_from_namelist(sp, spnlp, &extlist, ep) == -1) {
2N/A Free(mp);
2N/A return (-1);
2N/A }
2N/A
2N/A metafreenamelist(spnlp);
2N/A
2N/A if (getenv(META_SP_DEBUG)) {
2N/A meta_sp_debug("meta_sp_attach: list of used extents:\n");
2N/A meta_sp_list_dump(extlist);
2N/A }
2N/A
2N/A meta_sp_list_freefill(&extlist, metagetsize(compnp, ep));
2N/A
2N/A assert(mp->un_numexts >= 1);
2N/A numexts = meta_sp_alloc_by_len(sp, np, &extlist, &grow_len,
2N/A mp->un_ext[mp->un_numexts - 1].un_poff,
2N/A (alignment > 0) ? alignment :
2N/A meta_sp_get_default_alignment(sp, compnp, ep));
2N/A
2N/A if (numexts == -1) {
2N/A Free(mp);
2N/A return (mdmderror(ep, MDE_SP_NOSPACE, 0, np->cname));
2N/A }
2N/A
2N/A /* allocate new unit structure and copy in old unit */
2N/A if ((new_un = meta_sp_updateunit(np, mp, extlist,
2N/A grow_len, numexts, ep)) == NULL) {
2N/A Free(mp);
2N/A return (-1);
2N/A }
2N/A Free(mp);
2N/A
2N/A /* If running in dryrun mode (-n option), we're done here */
2N/A if ((options & MDCMD_DOIT) == 0) {
2N/A if (options & MDCMD_PRINT) {
2N/A (void) printf(dgettext(TEXT_DOMAIN,
2N/A "%s: Soft Partition would grow\n"),
2N/A np->cname);
2N/A (void) fflush(stdout);
2N/A }
2N/A return (0);
2N/A }
2N/A
2N/A if (getenv(META_SP_DEBUG)) {
2N/A meta_sp_debug("meta_sp_attach: updated unit structure:\n");
2N/A meta_sp_printunit(new_un);
2N/A }
2N/A
2N/A assert(new_un != NULL);
2N/A
2N/A (void) memset(&grow_params, 0, sizeof (grow_params));
2N/A if (new_un->c.un_total_blocks > MD_MAX_BLKS_FOR_SMALL_DEVS) {
2N/A grow_params.options = MD_CRO_64BIT;
2N/A new_un->c.un_revision |= MD_64BIT_META_DEV;
2N/A } else {
2N/A grow_params.options = MD_CRO_32BIT;
2N/A new_un->c.un_revision &= ~MD_64BIT_META_DEV;
2N/A }
2N/A grow_params.mnum = MD_SID(new_un);
2N/A grow_params.size = new_un->c.un_size;
2N/A grow_params.mdp = (uintptr_t)new_un;
2N/A MD_SETDRIVERNAME(&grow_params, MD_SP, MD_MIN2SET(grow_params.mnum));
2N/A
2N/A if (metaioctl(MD_IOCGROW, &grow_params, &grow_params.mde,
2N/A np->cname) != 0) {
2N/A (void) mdstealerror(ep, &grow_params.mde);
2N/A return (-1);
2N/A }
2N/A
2N/A /* update all watermarks */
2N/A
2N/A if ((msp = meta_get_sp(sp, np, ep)) == NULL)
2N/A return (-1);
2N/A if (meta_sp_update_wm(sp, msp, extlist, ep) < 0)
2N/A return (-1);
2N/A
2N/A
2N/A /* second phase of commit, set status to MD_SP_OK */
2N/A if (meta_sp_setstatus(sp, &(MD_SID(new_un)), 1, MD_SP_OK, ep) < 0)
2N/A return (-1);
2N/A
2N/A meta_invalidate_name(np);
2N/A
2N/A if (options & MDCMD_PRINT) {
2N/A (void) printf(dgettext(TEXT_DOMAIN,
2N/A "%s: Soft Partition has been grown\n"),
2N/A np->cname);
2N/A (void) fflush(stdout);
2N/A }
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * **************************************************************************
2N/A * Recovery (metarecover) Functions *
2N/A * **************************************************************************
2N/A */
2N/A
2N/A/*
2N/A * FUNCTION: meta_recover_sp()
2N/A * INPUT: sp - the name of the set we are recovering on
2N/A * compnp - name pointer for device we are recovering on
2N/A * argc - argument count
2N/A * argv - left over arguments not parsed by metarecover command
2N/A * options - metarecover options
2N/A * OUTPUT: ep - return error pointer
2N/A * RETURNS: int - 0 - success, -1 - error
2N/A * PURPOSE: parse soft partitioning-specific metarecover options and
2N/A * dispatch to the appropriate function to handle recovery.
2N/A */
2N/Aint
2N/Ameta_recover_sp(
2N/A mdsetname_t *sp,
2N/A mdname_t *compnp,
2N/A int argc,
2N/A char *argv[],
2N/A mdcmdopts_t options,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A md_set_desc *sd;
2N/A
2N/A if (argc > 1) {
2N/A (void) meta_cook_syntax(ep, MDE_SYNTAX, compnp->cname,
2N/A argc, argv);
2N/A return (-1);
2N/A }
2N/A
2N/A /*
2N/A * For a MN set, this operation must be performed on the master
2N/A * as it is responsible for maintaining the watermarks
2N/A */
2N/A if (!metaislocalset(sp)) {
2N/A if ((sd = metaget_setdesc(sp, ep)) == NULL)
2N/A return (-1);
2N/A if (MD_MNSET_DESC(sd) && !sd->sd_mn_am_i_master) {
2N/A (void) mddserror(ep, MDE_DS_MASTER_ONLY, sp->setno,
2N/A sd->sd_mn_master_nodenm, NULL, NULL);
2N/A return (-1);
2N/A }
2N/A }
2N/A if (argc == 0) {
2N/A /*
2N/A * if no additional arguments are passed, metarecover should
2N/A * validate both on-disk and metadb structures as well as
2N/A * checking that both are consistent with each other
2N/A */
2N/A if (meta_sp_validate_wm(sp, compnp, options, ep) < 0)
2N/A return (-1);
2N/A if (meta_sp_validate_unit(sp, compnp, options, ep) < 0)
2N/A return (-1);
2N/A if (meta_sp_validate_wm_and_unit(sp, compnp, options, ep) < 0)
2N/A return (-1);
2N/A } else if (strcmp(argv[0], "-d") == 0) {
2N/A /*
2N/A * Ensure that there is no existing valid record for this
2N/A * soft-partition. If there is we have nothing to do.
2N/A */
2N/A if (meta_sp_validate_unit(sp, compnp, options, ep) == 0)
2N/A return (-1);
2N/A /* validate and recover from on-disk structures */
2N/A if (meta_sp_validate_wm(sp, compnp, options, ep) < 0)
2N/A return (-1);
2N/A if (meta_sp_recover_from_wm(sp, compnp, options, ep) < 0)
2N/A return (-1);
2N/A } else if (strcmp(argv[0], "-m") == 0) {
2N/A /* validate and recover from metadb structures */
2N/A if (meta_sp_validate_unit(sp, compnp, options, ep) < 0)
2N/A return (-1);
2N/A if (meta_sp_recover_from_unit(sp, compnp, options, ep) < 0)
2N/A return (-1);
2N/A } else {
2N/A /* syntax error */
2N/A (void) meta_cook_syntax(ep, MDE_SYNTAX, compnp->cname,
2N/A argc, argv);
2N/A return (-1);
2N/A }
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * FUNCTION: meta_sp_display_exthdr()
2N/A * INPUT: none
2N/A * OUTPUT: none
2N/A * RETURNS: void
2N/A * PURPOSE: print header line for sp_ext_node_t information. to be used
2N/A * in conjunction with meta_sp_display_ext().
2N/A */
2N/Astatic void
2N/Ameta_sp_display_exthdr(void)
2N/A{
2N/A (void) printf("%20s %5s %7s %20s %20s\n",
2N/A dgettext(TEXT_DOMAIN, "Name"),
2N/A dgettext(TEXT_DOMAIN, "Seq#"),
2N/A dgettext(TEXT_DOMAIN, "Type"),
2N/A dgettext(TEXT_DOMAIN, "Offset"),
2N/A dgettext(TEXT_DOMAIN, "Length"));
2N/A}
2N/A
2N/A
2N/A/*
2N/A * FUNCTION: meta_sp_display_ext()
2N/A * INPUT: ext - extent to display
2N/A * OUTPUT: none
2N/A * RETURNS: void
2N/A * PURPOSE: print selected fields from sp_ext_node_t.
2N/A */
2N/Astatic void
2N/Ameta_sp_display_ext(sp_ext_node_t *ext)
2N/A{
2N/A /* print extent information */
2N/A if (ext->ext_namep != NULL)
2N/A (void) printf("%20s ", ext->ext_namep->cname);
2N/A else
2N/A (void) printf("%20s ", "NONE");
2N/A
2N/A (void) printf("%5u ", ext->ext_seq);
2N/A
2N/A switch (ext->ext_type) {
2N/A case EXTTYP_ALLOC:
2N/A (void) printf("%7s ", "ALLOC");
2N/A break;
2N/A case EXTTYP_FREE:
2N/A (void) printf("%7s ", "FREE");
2N/A break;
2N/A case EXTTYP_RESERVED:
2N/A (void) printf("%7s ", "RESV");
2N/A break;
2N/A case EXTTYP_END:
2N/A (void) printf("%7s ", "END");
2N/A break;
2N/A default:
2N/A (void) printf("%7s ", "INVLD");
2N/A break;
2N/A }
2N/A
2N/A (void) printf("%20llu %20llu\n", ext->ext_offset, ext->ext_length);
2N/A}
2N/A
2N/A
2N/A/*
2N/A * FUNCTION: meta_sp_checkseq()
2N/A * INPUT: extlist - list of extents to be checked
2N/A * OUTPUT: none
2N/A * RETURNS: int - 0 - success, -1 - error
2N/A * PURPOSE: check soft partition sequence numbers. this function assumes
2N/A * that a list of extents representing 1 or more soft partitions
2N/A * is passed in sorted in sequence number order. within a
2N/A * single soft partition, there may not be any missing or
2N/A * duplicate sequence numbers.
2N/A */
2N/Astatic int
2N/Ameta_sp_checkseq(sp_ext_node_t *extlist)
2N/A{
2N/A sp_ext_node_t *ext;
2N/A
2N/A assert(extlist != NULL);
2N/A
2N/A for (ext = extlist;
2N/A ext->ext_next != NULL && ext->ext_next->ext_type == EXTTYP_ALLOC;
2N/A ext = ext->ext_next) {
2N/A if (ext->ext_next->ext_namep != NULL &&
2N/A strcmp(ext->ext_next->ext_namep->cname,
2N/A ext->ext_namep->cname) != 0)
2N/A continue;
2N/A
2N/A if (ext->ext_next->ext_seq != ext->ext_seq + 1) {
2N/A (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
2N/A "%s: sequence numbers are "
2N/A "incorrect: %d should be %d\n"),
2N/A ext->ext_next->ext_namep->cname,
2N/A ext->ext_next->ext_seq, ext->ext_seq + 1);
2N/A return (-1);
2N/A }
2N/A }
2N/A return (0);
2N/A}
2N/A
2N/A
2N/A/*
2N/A * FUNCTION: meta_sp_resolve_name_conflict()
2N/A * INPUT: sp - name of set we're are recovering in.
2N/A * old_np - name pointer of soft partition we found on disk.
2N/A * OUTPUT: new_np - name pointer for new soft partition name.
2N/A * ep - error pointer returned.
2N/A * RETURNS: int - 0 - name not replace, 1 - name replaced, -1 - error
2N/A * PURPOSE: Check to see if the name of one of the soft partitions we found
2N/A * on disk already exists in the metadb. If so, prompt for a new
2N/A * name. In addition, we keep a static array of names that
2N/A * will be recovered from this device since these names don't
2N/A * exist in the configuration at this point but cannot be
2N/A * recovered more than once.
2N/A */
2N/Astatic int
2N/Ameta_sp_resolve_name_conflict(
2N/A mdsetname_t *sp,
2N/A mdname_t *old_np,
2N/A mdname_t **new_np,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A char yesno[255];
2N/A char *yes;
2N/A char newname[MD_SP_MAX_DEVNAME_PLUS_1];
2N/A int nunits;
2N/A static int *used_names = NULL;
2N/A
2N/A assert(old_np != NULL);
2N/A
2N/A if (used_names == NULL) {
2N/A if ((nunits = meta_get_nunits(ep)) < 0)
2N/A return (-1);
2N/A used_names = Zalloc(nunits * sizeof (int));
2N/A }
2N/A
2N/A /* see if it exists already */
2N/A if (used_names[MD_MIN2UNIT(meta_getminor(old_np->dev))] == 0 &&
2N/A metagetmiscname(old_np, ep) == NULL) {
2N/A if (! mdismderror(ep, MDE_UNIT_NOT_SETUP))
2N/A return (-1);
2N/A else {
2N/A used_names[MD_MIN2UNIT(meta_getminor(old_np->dev))] = 1;
2N/A mdclrerror(ep);
2N/A return (0);
2N/A }
2N/A }
2N/A
2N/A /* name exists, ask the user for a new one */
2N/A (void) printf(dgettext(TEXT_DOMAIN,
2N/A "WARNING: A soft partition named %s was found in the extent\n"
2N/A "headers, but this name already exists in the metadb "
2N/A "configuration.\n"
2N/A "In order to continue recovery you must supply\n"
2N/A "a new name for this soft partition.\n"), old_np->cname);
2N/A (void) printf(dgettext(TEXT_DOMAIN,
2N/A "Would you like to continue and supply a new name? (yes/no) "));
2N/A
2N/A (void) fflush(stdout);
2N/A if ((fgets(yesno, sizeof (yesno), stdin) == NULL) ||
2N/A (strlen(yesno) == 1))
2N/A (void) snprintf(yesno, sizeof (yesno), "%s\n",
2N/A dgettext(TEXT_DOMAIN, "no"));
2N/A yes = dgettext(TEXT_DOMAIN, "yes");
2N/A if (strncasecmp(yesno, yes, strlen(yesno) - 1) != 0) {
2N/A return (-1);
2N/A }
2N/A
2N/A (void) fflush(stdin);
2N/A
2N/A /* get the new name */
2N/A for (;;) {
2N/A (void) printf(dgettext(TEXT_DOMAIN, "Please enter a new name "
2N/A "for this soft partition (dXXXX) "));
2N/A (void) fflush(stdout);
2N/A if (fgets(newname, MD_SP_MAX_DEVNAME_PLUS_1, stdin) == NULL)
2N/A (void) strcpy(newname, "");
2N/A
2N/A /* remove newline character */
2N/A if (newname[strlen(newname) - 1] == '\n')
2N/A newname[strlen(newname) - 1] = '\0';
2N/A
2N/A if (!(is_metaname(newname)) ||
2N/A (meta_init_make_device(&sp, newname, ep) <= 0)) {
2N/A (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
2N/A "Invalid metadevice name\n"));
2N/A (void) fflush(stderr);
2N/A continue;
2N/A }
2N/A
2N/A if ((*new_np = metaname(&sp, newname,
2N/A META_DEVICE, ep)) == NULL) {
2N/A (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
2N/A "Invalid metadevice name\n"));
2N/A (void) fflush(stderr);
2N/A continue;
2N/A }
2N/A
2N/A assert(MD_MIN2UNIT(meta_getminor((*new_np)->dev)) < nunits);
2N/A /* make sure the name isn't already being used */
2N/A if (used_names[MD_MIN2UNIT(meta_getminor((*new_np)->dev))] ||
2N/A metagetmiscname(*new_np, ep) != NULL) {
2N/A (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
2N/A "That name already exists\n"));
2N/A continue;
2N/A } else if (! mdismderror(ep, MDE_UNIT_NOT_SETUP))
2N/A return (-1);
2N/A
2N/A break;
2N/A }
2N/A
2N/A /* got a new name, place in used array and return */
2N/A used_names[MD_MIN2UNIT(meta_getminor((*new_np)->dev))] = 1;
2N/A mdclrerror(ep);
2N/A return (1);
2N/A}
2N/A
2N/A/*
2N/A * FUNCTION: meta_sp_validate_wm()
2N/A * INPUT: sp - set name we are recovering in
2N/A * compnp - name pointer for device we are recovering from
2N/A * options - metarecover options
2N/A * OUTPUT: ep - error pointer returned
2N/A * RETURNS: int - 0 - success, -1 - error
2N/A * PURPOSE: validate and display watermark configuration. walk the
2N/A * on-disk watermark structures and validate the information
2N/A * found within. since a watermark configuration is
2N/A * "self-defining", the act of traversing the watermarks
2N/A * is part of the validation process.
2N/A */
2N/Astatic int
2N/Ameta_sp_validate_wm(
2N/A mdsetname_t *sp,
2N/A mdname_t *compnp,
2N/A mdcmdopts_t options,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A sp_ext_node_t *extlist = NULL;
2N/A sp_ext_node_t *ext;
2N/A int num_sps = 0;
2N/A int rval;
2N/A
2N/A if ((options & MDCMD_VERBOSE) != 0)
2N/A (void) printf(dgettext(TEXT_DOMAIN,
2N/A "Verifying on-disk structures on %s.\n"),
2N/A compnp->cname);
2N/A
2N/A /*
2N/A * for each watermark, build an ext_node, place on list.
2N/A */
2N/A rval = meta_sp_extlist_from_wm(sp, compnp, &extlist,
2N/A meta_sp_cmp_by_nameseq, ep);
2N/A
2N/A if ((options & MDCMD_VERBOSE) != 0) {
2N/A /* print out what we found */
2N/A if (extlist == NULL)
2N/A (void) printf(dgettext(TEXT_DOMAIN,
2N/A "No extent headers found on %s.\n"),
2N/A compnp->cname);
2N/A else {
2N/A (void) printf(dgettext(TEXT_DOMAIN,
2N/A "The following extent headers were found on %s.\n"),
2N/A compnp->cname);
2N/A meta_sp_display_exthdr();
2N/A }
2N/A for (ext = extlist; ext != NULL; ext = ext->ext_next)
2N/A meta_sp_display_ext(ext);
2N/A }
2N/A
2N/A if (rval < 0) {
2N/A (void) printf(dgettext(TEXT_DOMAIN,
2N/A "%s: On-disk structures invalid or "
2N/A "no soft partitions found.\n"),
2N/A compnp->cname);
2N/A return (-1);
2N/A }
2N/A
2N/A assert(extlist != NULL);
2N/A
2N/A /* count number of soft partitions */
2N/A for (ext = extlist;
2N/A ext != NULL && ext->ext_type == EXTTYP_ALLOC;
2N/A ext = ext->ext_next) {
2N/A if (ext->ext_next != NULL &&
2N/A ext->ext_next->ext_namep != NULL &&
2N/A strcmp(ext->ext_next->ext_namep->cname,
2N/A ext->ext_namep->cname) == 0)
2N/A continue;
2N/A num_sps++;
2N/A }
2N/A
2N/A if ((options & MDCMD_VERBOSE) != 0)
2N/A (void) printf(dgettext(TEXT_DOMAIN,
2N/A "Found %d soft partition(s) on %s.\n"), num_sps,
2N/A compnp->cname);
2N/A
2N/A if (num_sps == 0) {
2N/A (void) printf(dgettext(TEXT_DOMAIN,
2N/A "%s: No soft partitions.\n"), compnp->cname);
2N/A return (mdmderror(ep, MDE_RECOVER_FAILED, 0, compnp->cname));
2N/A }
2N/A
2N/A /* check sequence numbers */
2N/A if ((options & MDCMD_VERBOSE) != 0)
2N/A (void) printf(dgettext(TEXT_DOMAIN,
2N/A "Checking sequence numbers.\n"));
2N/A
2N/A if (meta_sp_checkseq(extlist) != 0)
2N/A return (mdmderror(ep, MDE_RECOVER_FAILED, 0, compnp->cname));
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * FUNCTION: meta_sp_validate_unit()
2N/A * INPUT: sp - name of set we are recovering in
2N/A * compnp - name of component we are recovering from
2N/A * options - metarecover options
2N/A * OUTPUT: ep - error pointer returned
2N/A * RETURNS: int - 0 - success, -1 - error
2N/A * PURPOSE: validate and display metadb configuration. begin by getting
2N/A * all soft partitions built on the specified component. get
2N/A * the unit structure for each one and validate the fields within.
2N/A */
2N/Astatic int
2N/Ameta_sp_validate_unit(
2N/A mdsetname_t *sp,
2N/A mdname_t *compnp,
2N/A mdcmdopts_t options,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A md_sp_t *msp;
2N/A mdnamelist_t *spnlp = NULL;
2N/A mdnamelist_t *namep = NULL;
2N/A int count;
2N/A uint_t extn;
2N/A sp_ext_length_t size;
2N/A
2N/A if ((options & MDCMD_VERBOSE) != 0)
2N/A (void) printf(dgettext(TEXT_DOMAIN,
2N/A "%s: Validating soft partition metadb entries.\n"),
2N/A compnp->cname);
2N/A
2N/A if ((size = metagetsize(compnp, ep)) == MD_DISKADDR_ERROR)
2N/A return (-1);
2N/A
2N/A /* get all soft partitions on component */
2N/A count = meta_sp_get_by_component(sp, compnp, &spnlp, 0, ep);
2N/A
2N/A if (count == 0) {
2N/A (void) printf(dgettext(TEXT_DOMAIN,
2N/A "%s: No soft partitions.\n"), compnp->cname);
2N/A return (mdmderror(ep, MDE_RECOVER_FAILED, 0, compnp->cname));
2N/A } else if (count < 0) {
2N/A return (-1);
2N/A }
2N/A
2N/A /* Now go through the soft partitions and check each one */
2N/A for (namep = spnlp; namep != NULL; namep = namep->next) {
2N/A mdname_t *curnp = namep->namep;
2N/A sp_ext_offset_t curvoff;
2N/A
2N/A /* get the unit structure */
2N/A if ((msp = meta_get_sp_common(sp, curnp, 0, ep)) == NULL)
2N/A return (-1);
2N/A
2N/A /* verify generic unit structure parameters */
2N/A if ((options & MDCMD_VERBOSE) != 0)
2N/A (void) printf(dgettext(TEXT_DOMAIN,
2N/A "\nVerifying device %s.\n"),
2N/A curnp->cname);
2N/A
2N/A /*
2N/A * MD_SP_LAST is an invalid state and is always the
2N/A * highest numbered.
2N/A */
2N/A if (msp->status >= MD_SP_LAST) {
2N/A (void) printf(dgettext(TEXT_DOMAIN,
2N/A "%s: status value %u is out of range.\n"),
2N/A curnp->cname, msp->status);
2N/A return (mdmderror(ep, MDE_RECOVER_FAILED,
2N/A 0, curnp->cname));
2N/A } else if ((options & MDCMD_VERBOSE) != 0) {
2N/A uint_t tstate = 0;
2N/A
2N/A if (metaismeta(msp->compnamep)) {
2N/A if (meta_get_tstate(msp->common.namep->dev,
2N/A &tstate, ep) != 0)
2N/A return (-1);
2N/A }
2N/A (void) printf(dgettext(TEXT_DOMAIN,
2N/A "%s: Status \"%s\" is valid.\n"),
2N/A curnp->cname, meta_sp_status_to_name(msp->status,
2N/A tstate & MD_DEV_ERRORED));
2N/A }
2N/A
2N/A /* Now verify each extent */
2N/A if ((options & MDCMD_VERBOSE) != 0)
2N/A (void) printf("%14s %21s %21s %21s\n",
2N/A dgettext(TEXT_DOMAIN, "Extent Number"),
2N/A dgettext(TEXT_DOMAIN, "Virtual Offset"),
2N/A dgettext(TEXT_DOMAIN, "Physical Offset"),
2N/A dgettext(TEXT_DOMAIN, "Length"));
2N/A
2N/A curvoff = 0ULL;
2N/A for (extn = 0; extn < msp->ext.ext_len; extn++) {
2N/A md_sp_ext_t *extp = &msp->ext.ext_val[extn];
2N/A
2N/A if ((options & MDCMD_VERBOSE) != 0)
2N/A (void) printf("%14u %21llu %21llu %21llu\n",
2N/A extn, extp->voff, extp->poff, extp->len);
2N/A
2N/A if (extp->voff != curvoff) {
2N/A (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
2N/A "%s: virtual offset for extent %u "
2N/A "is inconsistent, expected %llu, "
2N/A "got %llu.\n"), curnp->cname, extn,
2N/A curvoff, extp->voff);
2N/A return (mdmderror(ep, MDE_RECOVER_FAILED,
2N/A 0, compnp->cname));
2N/A }
2N/A
2N/A /* make sure extent does not drop off the end */
2N/A if ((extp->poff + extp->len) == size) {
2N/A (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
2N/A "%s: extent %u at offset %llu, "
2N/A "length %llu exceeds the size of the "
2N/A "device, %llu.\n"), curnp->cname,
2N/A extn, extp->poff, extp->len, size);
2N/A return (mdmderror(ep, MDE_RECOVER_FAILED,
2N/A 0, compnp->cname));
2N/A }
2N/A
2N/A curvoff += extp->len;
2N/A }
2N/A }
2N/A if (options & MDCMD_PRINT) {
2N/A (void) printf(dgettext(TEXT_DOMAIN,
2N/A "%s: Soft Partition metadb configuration is valid\n"),
2N/A compnp->cname);
2N/A }
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * FUNCTION: meta_sp_validate_wm_and_unit()
2N/A * INPUT: sp - name of set we are recovering in
2N/A * compnp - name of device we are recovering from
2N/A * options - metarecover options
2N/A * OUTPUT: ep - error pointer returned
2N/A * RETURNS: int - 0 - success, -1 error
2N/A * PURPOSE: cross-validate and display watermarks and metadb records.
2N/A * get both the unit structures for the soft partitions built
2N/A * on the specified component and the watermarks found on that
2N/A * component and check to make sure they are consistent with
2N/A * each other.
2N/A */
2N/Astatic int
2N/Ameta_sp_validate_wm_and_unit(
2N/A mdsetname_t *sp,
2N/A mdname_t *np,
2N/A mdcmdopts_t options,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A sp_ext_node_t *wmlist = NULL;
2N/A sp_ext_node_t *unitlist = NULL;
2N/A sp_ext_node_t *unitext;
2N/A sp_ext_node_t *wmext;
2N/A sp_ext_offset_t tmpunitoff;
2N/A mdnamelist_t *spnlp = NULL;
2N/A int count;
2N/A int rval = 0;
2N/A int verbose = (options & MDCMD_VERBOSE);
2N/A
2N/A /* get unit structure list */
2N/A count = meta_sp_get_by_component(sp, np, &spnlp, 0, ep);
2N/A if (count <= 0)
2N/A return (-1);
2N/A
2N/A meta_sp_list_insert(NULL, NULL, &unitlist,
2N/A metagetsize(np, ep) - MD_SP_WMSIZE, MD_SP_WMSIZE,
2N/A EXTTYP_END, 0, EXTFLG_UPDATE, meta_sp_cmp_by_offset);
2N/A
2N/A if (meta_sp_extlist_from_namelist(sp, spnlp, &unitlist, ep) == -1) {
2N/A metafreenamelist(spnlp);
2N/A return (-1);
2N/A }
2N/A
2N/A metafreenamelist(spnlp);
2N/A
2N/A meta_sp_list_freefill(&unitlist, metagetsize(np, ep));
2N/A
2N/A if (meta_sp_extlist_from_wm(sp, np, &wmlist,
2N/A meta_sp_cmp_by_offset, ep) < 0) {
2N/A meta_sp_list_free(&unitlist);
2N/A return (-1);
2N/A }
2N/A
2N/A if (getenv(META_SP_DEBUG)) {
2N/A meta_sp_debug("meta_sp_validate_wm_and_unit: unit list:\n");
2N/A meta_sp_list_dump(unitlist);
2N/A meta_sp_debug("meta_sp_validate_wm_and_unit: wm list:\n");
2N/A meta_sp_list_dump(wmlist);
2N/A }
2N/A
2N/A /*
2N/A * step through both lists and compare allocated nodes. Free
2N/A * nodes and end watermarks may differ between the two but
2N/A * that's generally ok, and if they're wrong will typically
2N/A * cause misplaced allocated extents.
2N/A */
2N/A if (verbose)
2N/A (void) printf(dgettext(TEXT_DOMAIN, "\n%s: Verifying metadb "
2N/A "allocations match extent headers.\n"), np->cname);
2N/A
2N/A unitext = unitlist;
2N/A wmext = wmlist;
2N/A while ((wmext != NULL) && (unitext != NULL)) {
2N/A /* find next allocated extents in each list */
2N/A while (wmext != NULL && wmext->ext_type != EXTTYP_ALLOC)
2N/A wmext = wmext->ext_next;
2N/A
2N/A while (unitext != NULL && unitext->ext_type != EXTTYP_ALLOC)
2N/A unitext = unitext->ext_next;
2N/A
2N/A if (wmext == NULL || unitext == NULL)
2N/A break;
2N/A
2N/A if (verbose) {
2N/A (void) printf(dgettext(TEXT_DOMAIN,
2N/A "Metadb extent:\n"));
2N/A meta_sp_display_exthdr();
2N/A meta_sp_display_ext(unitext);
2N/A (void) printf(dgettext(TEXT_DOMAIN,
2N/A "Extent header extent:\n"));
2N/A meta_sp_display_exthdr();
2N/A meta_sp_display_ext(wmext);
2N/A (void) printf("\n");
2N/A }
2N/A
2N/A if (meta_sp_validate_exts(np, wmext, unitext, ep) < 0)
2N/A rval = -1;
2N/A
2N/A /*
2N/A * if the offsets aren't equal, only increment the
2N/A * lowest one in hopes of getting the lists back in sync.
2N/A */
2N/A tmpunitoff = unitext->ext_offset;
2N/A if (unitext->ext_offset <= wmext->ext_offset)
2N/A unitext = unitext->ext_next;
2N/A if (wmext->ext_offset <= tmpunitoff)
2N/A wmext = wmext->ext_next;
2N/A }
2N/A
2N/A /*
2N/A * if both lists aren't at the end then there are extra
2N/A * allocated nodes in one of them.
2N/A */
2N/A if (wmext != NULL) {
2N/A (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
2N/A "%s: extent headers contain allocations not in "
2N/A "the metadb\n\n"), np->cname);
2N/A rval = -1;
2N/A }
2N/A
2N/A if (unitext != NULL) {
2N/A (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
2N/A "%s: metadb contains allocations not in the extent "
2N/A "headers\n\n"), np->cname);
2N/A rval = -1;
2N/A }
2N/A
2N/A if (options & MDCMD_PRINT) {
2N/A if (rval == 0) {
2N/A (void) printf(dgettext(TEXT_DOMAIN,
2N/A "%s: Soft Partition metadb matches extent "
2N/A "header configuration\n"), np->cname);
2N/A } else {
2N/A (void) printf(dgettext(TEXT_DOMAIN,
2N/A "%s: Soft Partition metadb does not match extent "
2N/A "header configuration\n"), np->cname);
2N/A }
2N/A }
2N/A
2N/A return (rval);
2N/A}
2N/A
2N/A/*
2N/A * FUNCTION: meta_sp_validate_exts()
2N/A * INPUT: compnp - name pointer for device we are recovering from
2N/A * wmext - extent node representing watermark
2N/A * unitext - extent node from unit structure
2N/A * OUTPUT: ep - return error pointer
2N/A * RETURNS: int - 0 - succes, mdmderror return code - error
2N/A * PURPOSE: Takes two extent nodes and checks them against each other.
2N/A * offset, length, sequence number, set, and name are compared.
2N/A */
2N/Astatic int
2N/Ameta_sp_validate_exts(
2N/A mdname_t *compnp,
2N/A sp_ext_node_t *wmext,
2N/A sp_ext_node_t *unitext,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A if (wmext->ext_offset != unitext->ext_offset) {
2N/A (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
2N/A "%s: unit structure and extent header offsets differ.\n"),
2N/A compnp->cname);
2N/A return (mdmderror(ep, MDE_RECOVER_FAILED, 0, compnp->cname));
2N/A }
2N/A
2N/A if (wmext->ext_length != unitext->ext_length) {
2N/A (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
2N/A "%s: unit structure and extent header lengths differ.\n"),
2N/A compnp->cname);
2N/A return (mdmderror(ep, MDE_RECOVER_FAILED, 0, compnp->cname));
2N/A }
2N/A
2N/A if (wmext->ext_seq != unitext->ext_seq) {
2N/A (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
2N/A "%s: unit structure and extent header sequence numbers "
2N/A "differ.\n"), compnp->cname);
2N/A return (mdmderror(ep, MDE_RECOVER_FAILED, 0, compnp->cname));
2N/A }
2N/A
2N/A if (wmext->ext_type != unitext->ext_type) {
2N/A (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
2N/A "%s: unit structure and extent header types differ.\n"),
2N/A compnp->cname);
2N/A return (mdmderror(ep, MDE_RECOVER_FAILED, 0, compnp->cname));
2N/A }
2N/A
2N/A /*
2N/A * If one has a set pointer and the other doesn't, error.
2N/A * If both extents have setnames, then make sure they match
2N/A * If both are NULL, it's ok, they match.
2N/A */
2N/A if ((unitext->ext_setp == NULL) ^ (wmext->ext_setp == NULL)) {
2N/A (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
2N/A "%s: unit structure and extent header set values "
2N/A "differ.\n"), compnp->cname);
2N/A return (mdmderror(ep, MDE_RECOVER_FAILED, 0, compnp->cname));
2N/A }
2N/A
2N/A if (unitext->ext_setp != NULL) {
2N/A if (strcmp(unitext->ext_setp->setname,
2N/A wmext->ext_setp->setname) != 0) {
2N/A (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
2N/A "%s: unit structure and extent header set names "
2N/A "differ.\n"), compnp->cname);
2N/A return (mdmderror(ep, MDE_RECOVER_FAILED,
2N/A 0, compnp->cname));
2N/A }
2N/A }
2N/A
2N/A /*
2N/A * If one has a name pointer and the other doesn't, error.
2N/A * If both extents have names, then make sure they match
2N/A * If both are NULL, it's ok, they match.
2N/A */
2N/A if ((unitext->ext_namep == NULL) ^ (wmext->ext_namep == NULL)) {
2N/A (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
2N/A "%s: unit structure and extent header name values "
2N/A "differ.\n"), compnp->cname);
2N/A return (mdmderror(ep, MDE_RECOVER_FAILED, 0, compnp->cname));
2N/A }
2N/A
2N/A if (unitext->ext_namep != NULL) {
2N/A if (strcmp(wmext->ext_namep->cname,
2N/A unitext->ext_namep->cname) != 0) {
2N/A (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
2N/A "%s: unit structure and extent header names "
2N/A "differ.\n"), compnp->cname);
2N/A return (mdmderror(ep, MDE_RECOVER_FAILED,
2N/A 0, compnp->cname));
2N/A }
2N/A }
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * FUNCTION: update_sp_status()
2N/A * INPUT: sp - name of set we are recovering in
2N/A * minors - pointer to an array of soft partition minor numbers
2N/A * num_sps - number of minor numbers in array
2N/A * status - new status to be applied to all soft parts in array
2N/A * mn_set - set if current set is a multi-node set
2N/A * OUTPUT: ep - return error pointer
2N/A * RETURNS: int - 0 - success, -1 - error
2N/A * PURPOSE: update status of soft partitions to new status. minors is an
2N/A * array of minor numbers to apply the new status to.
2N/A * If mn_set is set, a message is sent to all nodes in the
2N/A * cluster to update the status locally.
2N/A */
2N/Astatic int
2N/Aupdate_sp_status(
2N/A mdsetname_t *sp,
2N/A minor_t *minors,
2N/A int num_sps,
2N/A sp_status_t status,
2N/A bool_t mn_set,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A int i;
2N/A int err = 0;
2N/A
2N/A if (mn_set) {
2N/A md_mn_msg_sp_setstat_t sp_setstat_params;
2N/A int result;
2N/A md_mn_result_t *resp = NULL;
2N/A
2N/A for (i = 0; i < num_sps; i++) {
2N/A sp_setstat_params.sp_setstat_mnum = minors[i];
2N/A sp_setstat_params.sp_setstat_status = status;
2N/A
2N/A result = mdmn_send_message(sp->setno,
2N/A MD_MN_MSG_SP_SETSTAT, MD_MSGF_DEFAULT_FLAGS, 0,
2N/A (char *)&sp_setstat_params,
2N/A sizeof (sp_setstat_params),
2N/A &resp, ep);
2N/A if (resp != NULL) {
2N/A if (resp->mmr_exitval != 0)
2N/A err = -1;
2N/A free_result(resp);
2N/A }
2N/A if (result != 0) {
2N/A err = -1;
2N/A }
2N/A }
2N/A } else {
2N/A if (meta_sp_setstatus(sp, minors, num_sps, status, ep) < 0)
2N/A err = -1;
2N/A }
2N/A if (err < 0) {
2N/A (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
2N/A "Error updating status on recovered soft "
2N/A "partitions.\n"));
2N/A }
2N/A return (err);
2N/A}
2N/A
2N/A/*
2N/A * FUNCTION: meta_sp_recover_from_wm()
2N/A * INPUT: sp - name of set we are recovering in
2N/A * compnp - name pointer for component we are recovering from
2N/A * options - metarecover options
2N/A * OUTPUT: ep - return error pointer
2N/A * RETURNS: int - 0 - success, -1 - error
2N/A * PURPOSE: update metadb records to match watermarks. begin by getting
2N/A * an extlist representing all soft partitions on the component.
2N/A * then build a unit structure for each soft partition.
2N/A * notify user of changes, then commit each soft partition to
2N/A * the metadb one at a time in the "recovering" state. update
2N/A * any watermarks that may need it (to reflect possible name
2N/A * changes), and, finally, set the status of all recovered
2N/A * partitions to the "OK" state at once.
2N/A */
2N/Astatic int
2N/Ameta_sp_recover_from_wm(
2N/A mdsetname_t *sp,
2N/A mdname_t *compnp,
2N/A mdcmdopts_t options,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A sp_ext_node_t *extlist = NULL;
2N/A sp_ext_node_t *sp_list = NULL;
2N/A sp_ext_node_t *update_list = NULL;
2N/A sp_ext_node_t *ext;
2N/A sp_ext_node_t *sp_ext;
2N/A mp_unit_t *mp;
2N/A mp_unit_t **un_array;
2N/A int numexts = 0, num_sps = 0, i = 0;
2N/A int err = 0;
2N/A int not_recovered = 0;
2N/A int committed = 0;
2N/A sp_ext_length_t sp_length = 0LL;
2N/A mdnamelist_t *keynlp = NULL;
2N/A mdname_t *np;
2N/A mdname_t *new_np;
2N/A int new_name;
2N/A md_set_params_t set_params;
2N/A minor_t *minors = NULL;
2N/A char yesno[255];
2N/A char *yes;
2N/A bool_t mn_set = 0;
2N/A md_set_desc *sd;
2N/A mm_unit_t *mm;
2N/A md_set_mmown_params_t *ownpar = NULL;
2N/A int comp_is_mirror = 0;
2N/A
2N/A /*
2N/A * if this component appears in another metadevice already, do
2N/A * NOT recover from it.
2N/A */
2N/A if (meta_check_inmeta(sp, compnp, options, 0, -1, ep) != 0)
2N/A return (-1);
2N/A
2N/A /* set flag if dealing with a MN set */
2N/A if (!metaislocalset(sp)) {
2N/A if ((sd = metaget_setdesc(sp, ep)) == NULL) {
2N/A return (-1);
2N/A }
2N/A if (MD_MNSET_DESC(sd))
2N/A mn_set = 1;
2N/A }
2N/A /*
2N/A * for each watermark, build an ext_node, place on list.
2N/A */
2N/A if (meta_sp_extlist_from_wm(sp, compnp, &extlist,
2N/A meta_sp_cmp_by_nameseq, ep) < 0)
2N/A return (mdmderror(ep, MDE_RECOVER_FAILED, 0, compnp->cname));
2N/A
2N/A assert(extlist != NULL);
2N/A
2N/A /* count number of soft partitions */
2N/A for (ext = extlist;
2N/A ext != NULL && ext->ext_type == EXTTYP_ALLOC;
2N/A ext = ext->ext_next) {
2N/A if (ext->ext_next != NULL &&
2N/A ext->ext_next->ext_namep != NULL &&
2N/A strcmp(ext->ext_next->ext_namep->cname,
2N/A ext->ext_namep->cname) == 0)
2N/A continue;
2N/A num_sps++;
2N/A }
2N/A
2N/A /* allocate array of unit structure pointers */
2N/A un_array = Zalloc(num_sps * sizeof (mp_unit_t *));
2N/A
2N/A /*
2N/A * build unit structures from list of ext_nodes.
2N/A */
2N/A for (ext = extlist;
2N/A ext != NULL && ext->ext_type == EXTTYP_ALLOC;
2N/A ext = ext->ext_next) {
2N/A meta_sp_list_insert(ext->ext_setp, ext->ext_namep,
2N/A &sp_list, ext->ext_offset, ext->ext_length,
2N/A ext->ext_type, ext->ext_seq, ext->ext_flags,
2N/A meta_sp_cmp_by_nameseq);
2N/A
2N/A numexts++;
2N/A sp_length += ext->ext_length - MD_SP_WMSIZE;
2N/A
2N/A if (ext->ext_next != NULL &&
2N/A ext->ext_next->ext_namep != NULL &&
2N/A strcmp(ext->ext_next->ext_namep->cname,
2N/A ext->ext_namep->cname) == 0)
2N/A continue;
2N/A
2N/A /*
2N/A * if we made it here, we are at a soft partition
2N/A * boundary in the list.
2N/A */
2N/A if (getenv(META_SP_DEBUG)) {
2N/A meta_sp_debug("meta_recover_from_wm: dumping wm "
2N/A "list:\n");
2N/A meta_sp_list_dump(sp_list);
2N/A }
2N/A
2N/A assert(sp_list != NULL);
2N/A assert(sp_list->ext_namep != NULL);
2N/A
2N/A if ((new_name = meta_sp_resolve_name_conflict(sp,
2N/A sp_list->ext_namep, &new_np, ep)) < 0) {
2N/A err = 1;
2N/A goto out;
2N/A } else if (new_name) {
2N/A for (sp_ext = sp_list;
2N/A sp_ext != NULL;
2N/A sp_ext = sp_ext->ext_next) {
2N/A /*
2N/A * insert into the update list for
2N/A * watermark update.
2N/A */
2N/A meta_sp_list_insert(sp_ext->ext_setp,
2N/A new_np, &update_list, sp_ext->ext_offset,
2N/A sp_ext->ext_length, sp_ext->ext_type,
2N/A sp_ext->ext_seq, EXTFLG_UPDATE,
2N/A meta_sp_cmp_by_offset);
2N/A }
2N/A
2N/A }
2N/A if (options & MDCMD_DOIT) {
2N/A /* store name in namespace */
2N/A if (mn_set) {
2N/A /* send message to all nodes to return key */
2N/A md_mn_msg_addkeyname_t *send_params;
2N/A int result;
2N/A md_mn_result_t *resp = NULL;
2N/A int message_size;
2N/A
2N/A message_size = sizeof (*send_params) +
2N/A strlen(compnp->cname) + 1;
2N/A send_params = Zalloc(message_size);
2N/A send_params->addkeyname_setno = sp->setno;
2N/A (void) strcpy(&send_params->addkeyname_name[0],
2N/A compnp->cname);
2N/A result = mdmn_send_message(sp->setno,
2N/A MD_MN_MSG_ADDKEYNAME, MD_MSGF_DEFAULT_FLAGS,
2N/A 0, (char *)send_params, message_size, &resp,
2N/A ep);
2N/A Free(send_params);
2N/A if (resp != NULL) {
2N/A if (resp->mmr_exitval >= 0) {
2N/A compnp->key =
2N/A (mdkey_t)resp->mmr_exitval;
2N/A } else {
2N/A err = 1;
2N/A free_result(resp);
2N/A goto out;
2N/A }
2N/A free_result(resp);
2N/A }
2N/A if (result != 0) {
2N/A err = 1;
2N/A goto out;
2N/A }
2N/A (void) metanamelist_append(&keynlp, compnp);
2N/A } else {
2N/A if (add_key_name(sp, compnp, &keynlp,
2N/A ep) != 0) {
2N/A err = 1;
2N/A goto out;
2N/A }
2N/A }
2N/A }
2N/A
2N/A /* create the unit structure */
2N/A if ((mp = meta_sp_createunit(
2N/A (new_name) ? new_np : sp_list->ext_namep, compnp,
2N/A sp_list, numexts, sp_length, MD_SP_RECOVER, ep)) == NULL) {
2N/A err = 1;
2N/A goto out;
2N/A }
2N/A
2N/A if (getenv(META_SP_DEBUG)) {
2N/A meta_sp_debug("meta_sp_recover_from_wm: "
2N/A "printing newly created unit structure");
2N/A meta_sp_printunit(mp);
2N/A }
2N/A
2N/A /* place in unit structure array */
2N/A un_array[i++] = mp;
2N/A
2N/A /* free sp_list */
2N/A meta_sp_list_free(&sp_list);
2N/A sp_list = NULL;
2N/A numexts = 0;
2N/A sp_length = 0LL;
2N/A }
2N/A
2N/A /* display configuration updates */
2N/A (void) printf(dgettext(TEXT_DOMAIN,
2N/A "The following soft partitions were found and will be added to\n"
2N/A "your metadevice configuration.\n"));
2N/A (void) printf("%5s %15s %18s\n",
2N/A dgettext(TEXT_DOMAIN, "Name"),
2N/A dgettext(TEXT_DOMAIN, "Size"),
2N/A dgettext(TEXT_DOMAIN, "No. of Extents"));
2N/A for (i = 0; i < num_sps; i++) {
2N/A (void) printf("%5s%lu %15llu %9d\n", "d",
2N/A MD_MIN2UNIT(MD_SID(un_array[i])),
2N/A un_array[i]->un_length, un_array[i]->un_numexts);
2N/A }
2N/A
2N/A if (!(options & MDCMD_DOIT)) {
2N/A not_recovered = 1;
2N/A goto out;
2N/A }
2N/A
2N/A /* ask user for confirmation */
2N/A (void) printf(dgettext(TEXT_DOMAIN,
2N/A "WARNING: You are about to add one or more soft partition\n"
2N/A "metadevices to your metadevice configuration. If there\n"
2N/A "appears to be an error in the soft partition(s) displayed\n"
2N/A "above, do NOT proceed with this recovery operation.\n"));
2N/A (void) printf(dgettext(TEXT_DOMAIN,
2N/A "Are you sure you want to do this (yes/no)? "));
2N/A
2N/A (void) fflush(stdout);
2N/A if ((fgets(yesno, sizeof (yesno), stdin) == NULL) ||
2N/A (strlen(yesno) == 1))
2N/A (void) snprintf(yesno, sizeof (yesno), "%s\n",
2N/A dgettext(TEXT_DOMAIN, "no"));
2N/A yes = dgettext(TEXT_DOMAIN, "yes");
2N/A if (strncasecmp(yesno, yes, strlen(yesno) - 1) != 0) {
2N/A not_recovered = 1;
2N/A goto out;
2N/A }
2N/A
2N/A /* commit records one at a time */
2N/A for (i = 0; i < num_sps; i++) {
2N/A (void) memset(&set_params, 0, sizeof (set_params));
2N/A set_params.mnum = MD_SID(un_array[i]);
2N/A set_params.size = (un_array[i])->c.un_size;
2N/A set_params.mdp = (uintptr_t)(un_array[i]);
2N/A set_params.options =
2N/A meta_check_devicesize(un_array[i]->un_length);
2N/A if (set_params.options == MD_CRO_64BIT) {
2N/A un_array[i]->c.un_revision |= MD_64BIT_META_DEV;
2N/A } else {
2N/A un_array[i]->c.un_revision &= ~MD_64BIT_META_DEV;
2N/A }
2N/A MD_SETDRIVERNAME(&set_params, MD_SP,
2N/A MD_MIN2SET(set_params.mnum));
2N/A
2N/A np = metamnumname(&sp, MD_SID(un_array[i]), 0, ep);
2N/A
2N/A /*
2N/A * If this is an MN set, send the MD_IOCSET ioctl to all nodes
2N/A */
2N/A if (mn_set) {
2N/A md_mn_msg_iocset_t send_params;
2N/A int result;
2N/A md_mn_result_t *resp = NULL;
2N/A int mess_size;
2N/A
2N/A /*
2N/A * Calculate message size. md_mn_msg_iocset_t only
2N/A * contains one extent, so increment the size to
2N/A * include all extents
2N/A */
2N/A mess_size = sizeof (send_params) -
2N/A sizeof (mp_ext_t) +
2N/A (un_array[i]->un_numexts * sizeof (mp_ext_t));
2N/A
2N/A send_params.iocset_params = set_params;
2N/A (void) memcpy(&send_params.unit, un_array[i],
2N/A sizeof (*un_array[i]) - sizeof (mp_ext_t) +
2N/A (un_array[i]->un_numexts * sizeof (mp_ext_t)));
2N/A result = mdmn_send_message(sp->setno,
2N/A MD_MN_MSG_IOCSET, MD_MSGF_DEFAULT_FLAGS, 0,
2N/A (char *)&send_params, mess_size, &resp,
2N/A ep);
2N/A if (resp != NULL) {
2N/A if (resp->mmr_exitval != 0)
2N/A err = 1;
2N/A free_result(resp);
2N/A }
2N/A if (result != 0) {
2N/A err = 1;
2N/A }
2N/A } else {
2N/A if (metaioctl(MD_IOCSET, &set_params, &set_params.mde,
2N/A np->cname) != 0) {
2N/A err = 1;
2N/A }
2N/A }
2N/A
2N/A if (err == 1) {
2N/A (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
2N/A "%s: Error committing record to metadb.\n"),
2N/A np->cname);
2N/A goto out;
2N/A }
2N/A
2N/A /* note that we've committed a record */
2N/A if (!committed)
2N/A committed = 1;
2N/A
2N/A /* update any watermarks that need it */
2N/A if (update_list != NULL) {
2N/A md_sp_t *msp;
2N/A
2N/A /*
2N/A * Check to see if we're trying to create a partition
2N/A * on a mirror. If so we may have to enforce an
2N/A * ownership change before writing the watermark out.
2N/A */
2N/A if (metaismeta(compnp)) {
2N/A char *miscname;
2N/A
2N/A miscname = metagetmiscname(compnp, ep);
2N/A if (miscname != NULL)
2N/A comp_is_mirror = (strcmp(miscname,
2N/A MD_MIRROR) == 0);
2N/A else
2N/A comp_is_mirror = 0;
2N/A }
2N/A /*
2N/A * If this is a MN set and the component is a mirror,
2N/A * change ownership to this node in order to write the
2N/A * watermarks
2N/A */
2N/A if (mn_set && comp_is_mirror) {
2N/A mm = (mm_unit_t *)meta_get_unit(sp, compnp, ep);
2N/A if (mm == NULL) {
2N/A err = 1;
2N/A goto out;
2N/A } else {
2N/A err = meta_mn_change_owner(&ownpar,
2N/A sp->setno,
2N/A meta_getminor(compnp->dev),
2N/A sd->sd_mn_mynode->nd_nodeid,
2N/A MD_MN_MM_PREVENT_CHANGE |
2N/A MD_MN_MM_SPAWN_THREAD);
2N/A if (err != 0)
2N/A goto out;
2N/A }
2N/A }
2N/A
2N/A if ((msp = meta_get_sp(sp, np, ep)) == NULL) {
2N/A err = 1;
2N/A (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
2N/A "%s: Error updating extent headers.\n"),
2N/A np->cname);
2N/A goto out;
2N/A }
2N/A if (meta_sp_update_wm(sp, msp, update_list, ep) < 0) {
2N/A err = 1;
2N/A (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
2N/A "%s: Error updating extent headers "
2N/A "on disk.\n"), np->cname);
2N/A goto out;
2N/A }
2N/A }
2N/A /*
2N/A * If we have changed ownership earlier and prevented any
2N/A * ownership changes, we can now allow ownership changes
2N/A * again.
2N/A */
2N/A if (ownpar) {
2N/A (void) meta_mn_change_owner(&ownpar, sp->setno,
2N/A ownpar->d.mnum,
2N/A ownpar->d.owner,
2N/A MD_MN_MM_ALLOW_CHANGE | MD_MN_MM_SPAWN_THREAD);
2N/A }
2N/A }
2N/A
2N/A /* update status of all soft partitions to OK */
2N/A minors = Zalloc(num_sps * sizeof (minor_t));
2N/A for (i = 0; i < num_sps; i++)
2N/A minors[i] = MD_SID(un_array[i]);
2N/A
2N/A err = update_sp_status(sp, minors, num_sps, MD_SP_OK, mn_set, ep);
2N/A if (err != 0)
2N/A goto out;
2N/A
2N/A if (options & MDCMD_PRINT)
2N/A (void) printf(dgettext(TEXT_DOMAIN, "%s: "
2N/A "Soft Partitions recovered from device.\n"),
2N/A compnp->cname);
2N/Aout:
2N/A /* free memory */
2N/A if (extlist != NULL)
2N/A meta_sp_list_free(&extlist);
2N/A if (sp_list != NULL)
2N/A meta_sp_list_free(&sp_list);
2N/A if (update_list != NULL)
2N/A meta_sp_list_free(&update_list);
2N/A if (un_array != NULL) {
2N/A for (i = 0; i < num_sps; i++)
2N/A Free(un_array[i]);
2N/A Free(un_array);
2N/A }
2N/A if (minors != NULL)
2N/A Free(minors);
2N/A if (ownpar != NULL)
2N/A Free(ownpar);
2N/A (void) fflush(stdout);
2N/A
2N/A if ((keynlp != NULL) && (committed != 1)) {
2N/A /*
2N/A * if we haven't committed any softparts, either because of an
2N/A * error or because the user decided not to proceed, delete
2N/A * namelist key for the component
2N/A */
2N/A if (mn_set) {
2N/A mdnamelist_t *p;
2N/A
2N/A for (p = keynlp; (p != NULL); p = p->next) {
2N/A mdname_t *np = p->namep;
2N/A md_mn_msg_delkeyname_t send_params;
2N/A md_mn_result_t *resp = NULL;
2N/A
2N/A send_params.delkeyname_dev = np->dev;
2N/A send_params.delkeyname_setno = sp->setno;
2N/A send_params.delkeyname_key = np->key;
2N/A (void) mdmn_send_message(sp->setno,
2N/A MD_MN_MSG_DELKEYNAME, MD_MSGF_DEFAULT_FLAGS,
2N/A 0, (char *)&send_params,
2N/A sizeof (send_params),
2N/A &resp, ep);
2N/A if (resp != NULL) {
2N/A free_result(resp);
2N/A }
2N/A }
2N/A } else {
2N/A (void) del_key_names(sp, keynlp, NULL);
2N/A }
2N/A }
2N/A
2N/A metafreenamelist(keynlp);
2N/A
2N/A if (err)
2N/A return (mdmderror(ep, MDE_RECOVER_FAILED, 0, compnp->cname));
2N/A
2N/A if (not_recovered)
2N/A if (options & MDCMD_PRINT)
2N/A (void) printf(dgettext(TEXT_DOMAIN, "%s: "
2N/A "Soft Partitions NOT recovered from device.\n"),
2N/A compnp->cname);
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * FUNCTION: meta_sp_recover_from_unit()
2N/A * INPUT: sp - name of set we are recovering in
2N/A * compnp - name of component we are recovering from
2N/A * options - metarecover options
2N/A * OUTPUT: ep - return error pointer
2N/A * RETURNS: int - 0 - success, -1 - error
2N/A * PURPOSE: update watermarks to match metadb records. begin by getting
2N/A * a namelist representing all soft partitions on the specified
2N/A * component. then, build an extlist representing the soft
2N/A * partitions, filling in the freespace extents. notify user
2N/A * of changes, place all soft partitions into the "recovering"
2N/A * state and update the watermarks. finally, return all soft
2N/A * partitions to the "OK" state.
2N/A */
2N/Astatic int
2N/Ameta_sp_recover_from_unit(
2N/A mdsetname_t *sp,
2N/A mdname_t *compnp,
2N/A mdcmdopts_t options,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A mdnamelist_t *spnlp = NULL;
2N/A mdnamelist_t *nlp = NULL;
2N/A sp_ext_node_t *ext = NULL;
2N/A sp_ext_node_t *extlist = NULL;
2N/A int count;
2N/A char yesno[255];
2N/A char *yes;
2N/A int rval = 0;
2N/A minor_t *minors = NULL;
2N/A int i;
2N/A md_sp_t *msp;
2N/A md_set_desc *sd;
2N/A bool_t mn_set = 0;
2N/A daddr_t start_block;
2N/A
2N/A count = meta_sp_get_by_component(sp, compnp, &spnlp, 0, ep);
2N/A if (count <= 0)
2N/A return (-1);
2N/A
2N/A /* set flag if dealing with a MN set */
2N/A if (!metaislocalset(sp)) {
2N/A if ((sd = metaget_setdesc(sp, ep)) == NULL) {
2N/A return (-1);
2N/A }
2N/A if (MD_MNSET_DESC(sd))
2N/A mn_set = 1;
2N/A }
2N/A /*
2N/A * Save the XDR unit structure for one of the soft partitions;
2N/A * we'll use this later to provide metadevice context to
2N/A * update the watermarks so the device can be resolved by
2N/A * devid instead of dev_t.
2N/A */
2N/A if ((msp = meta_get_sp(sp, spnlp->namep, ep)) == NULL) {
2N/A metafreenamelist(spnlp);
2N/A return (-1);
2N/A }
2N/A
2N/A if ((start_block = meta_sp_get_start(sp, compnp, ep)) ==
2N/A MD_DISKADDR_ERROR) {
2N/A return (-1);
2N/A }
2N/A
2N/A meta_sp_list_insert(NULL, NULL, &extlist, 0ULL, start_block,
2N/A EXTTYP_RESERVED, 0, 0, meta_sp_cmp_by_offset);
2N/A meta_sp_list_insert(NULL, NULL, &extlist,
2N/A metagetsize(compnp, ep) - MD_SP_WMSIZE, MD_SP_WMSIZE,
2N/A EXTTYP_END, 0, EXTFLG_UPDATE, meta_sp_cmp_by_offset);
2N/A
2N/A if (meta_sp_extlist_from_namelist(sp, spnlp, &extlist, ep) == -1) {
2N/A metafreenamelist(spnlp);
2N/A return (-1);
2N/A }
2N/A
2N/A assert(extlist != NULL);
2N/A if ((options & MDCMD_VERBOSE) != 0) {
2N/A (void) printf(dgettext(TEXT_DOMAIN,
2N/A "Updating extent headers on device %s from metadb.\n\n"),
2N/A compnp->cname);
2N/A (void) printf(dgettext(TEXT_DOMAIN,
2N/A "The following extent headers will be written:\n"));
2N/A meta_sp_display_exthdr();
2N/A }
2N/A
2N/A meta_sp_list_freefill(&extlist, metagetsize(compnp, ep));
2N/A
2N/A for (ext = extlist; ext != NULL; ext = ext->ext_next) {
2N/A
2N/A /* mark every node for updating except the reserved space */
2N/A if (ext->ext_type != EXTTYP_RESERVED) {
2N/A ext->ext_flags |= EXTFLG_UPDATE;
2N/A
2N/A /* print extent information */
2N/A if ((options & MDCMD_VERBOSE) != 0)
2N/A meta_sp_display_ext(ext);
2N/A }
2N/A }
2N/A
2N/A /* request verification and then update all watermarks */
2N/A if ((options & MDCMD_DOIT) != 0) {
2N/A
2N/A (void) printf(dgettext(TEXT_DOMAIN,
2N/A "\nWARNING: You are about to overwrite portions of %s\n"
2N/A "with soft partition metadata. The extent headers will be\n"
2N/A "written to match the existing metadb configuration. If\n"
2N/A "the device was not previously setup with this\n"
2N/A "configuration, data loss may result.\n\n"),
2N/A compnp->cname);
2N/A (void) printf(dgettext(TEXT_DOMAIN,
2N/A "Are you sure you want to do this (yes/no)? "));
2N/A
2N/A (void) fflush(stdout);
2N/A if ((fgets(yesno, sizeof (yesno), stdin) == NULL) ||
2N/A (strlen(yesno) == 1))
2N/A (void) snprintf(yesno, sizeof (yesno),
2N/A "%s\n", dgettext(TEXT_DOMAIN, "no"));
2N/A yes = dgettext(TEXT_DOMAIN, "yes");
2N/A if (strncasecmp(yesno, yes, strlen(yesno) - 1) == 0) {
2N/A /* place soft partitions into recovering state */
2N/A minors = Zalloc(count * sizeof (minor_t));
2N/A for (nlp = spnlp, i = 0;
2N/A nlp != NULL && i < count;
2N/A nlp = nlp->next, i++) {
2N/A assert(nlp->namep != NULL);
2N/A minors[i] = meta_getminor(nlp->namep->dev);
2N/A }
2N/A if (update_sp_status(sp, minors, count,
2N/A MD_SP_RECOVER, mn_set, ep) != 0) {
2N/A rval = -1;
2N/A goto out;
2N/A }
2N/A
2N/A /* update the watermarks */
2N/A if (meta_sp_update_wm(sp, msp, extlist, ep) < 0) {
2N/A rval = -1;
2N/A goto out;
2N/A }
2N/A
2N/A if (options & MDCMD_PRINT) {
2N/A (void) printf(dgettext(TEXT_DOMAIN, "%s: "
2N/A "Soft Partitions recovered from metadb\n"),
2N/A compnp->cname);
2N/A }
2N/A
2N/A /* return soft partitions to the OK state */
2N/A if (update_sp_status(sp, minors, count,
2N/A MD_SP_OK, mn_set, ep) != 0) {
2N/A rval = -1;
2N/A goto out;
2N/A }
2N/A
2N/A rval = 0;
2N/A goto out;
2N/A }
2N/A }
2N/A
2N/A if (options & MDCMD_PRINT) {
2N/A (void) printf(dgettext(TEXT_DOMAIN,
2N/A "%s: Soft Partitions NOT recovered from metadb\n"),
2N/A compnp->cname);
2N/A }
2N/A
2N/Aout:
2N/A if (minors != NULL)
2N/A Free(minors);
2N/A metafreenamelist(spnlp);
2N/A meta_sp_list_free(&extlist);
2N/A (void) fflush(stdout);
2N/A return (rval);
2N/A}
2N/A
2N/A
2N/A/*
2N/A * FUNCTION: meta_sp_update_abr()
2N/A * INPUT: sp - name of set we are recovering in
2N/A * OUTPUT: ep - return error pointer
2N/A * RETURNS: int - 0 - success, -1 - error
2N/A * PURPOSE: update the ABR state for all soft partitions in the set. This
2N/A * is called when joining a set. It sends a message to the master
2N/A * node for each soft partition to get the value of tstate and
2N/A * then sets ABR ,if required, by opening the sp, setting ABR
2N/A * and then closing the sp. This approach is taken rather that
2N/A * just issuing the MD_MN_SET_CAP ioctl, in order to deal with
2N/A * the case when we have another node simultaneously unsetting ABR.
2N/A */
2N/Aint
2N/Ameta_sp_update_abr(
2N/A mdsetname_t *sp,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A mdnamelist_t *devnlp = NULL;
2N/A mdnamelist_t *p;
2N/A mdname_t *devnp = NULL;
2N/A md_unit_t *un;
2N/A char fname[MAXPATHLEN];
2N/A int mnum, fd;
2N/A volcap_t vc;
2N/A uint_t tstate;
2N/A
2N/A
2N/A if (meta_get_sp_names(sp, &devnlp, 0, ep) < 0) {
2N/A return (-1);
2N/A }
2N/A
2N/A /* Exit if no soft partitions in this set */
2N/A if (devnlp == NULL)
2N/A return (0);
2N/A
2N/A /* For each soft partition */
2N/A for (p = devnlp; (p != NULL); p = p->next) {
2N/A devnp = p->namep;
2N/A
2N/A /* check if this is a top level metadevice */
2N/A if ((un = meta_get_mdunit(sp, devnp, ep)) == NULL)
2N/A goto out;
2N/A if (MD_HAS_PARENT(MD_PARENT(un))) {
2N/A Free(un);
2N/A continue;
2N/A }
2N/A Free(un);
2N/A
2N/A /* Get tstate from Master */
2N/A if (meta_mn_send_get_tstate(devnp->dev, &tstate, ep) != 0) {
2N/A mdname_t *np;
2N/A np = metamnumname(&sp, meta_getminor(devnp->dev), 0,
2N/A ep);
2N/A if (np) {
2N/A md_perror(dgettext(TEXT_DOMAIN,
2N/A "Unable to get tstate for %s"), np->cname);
2N/A }
2N/A continue;
2N/A }
2N/A /* If not set on the master, nothing to do */
2N/A if (!(tstate & MD_ABR_CAP))
2N/A continue;
2N/A
2N/A mnum = meta_getminor(devnp->dev);
2N/A (void) snprintf(fname, MAXPATHLEN, "/dev/md/%s/rdsk/d%u",
2N/A sp->setname, (unsigned)MD_MIN2UNIT(mnum));
2N/A if ((fd = open(fname, O_RDWR, 0)) < 0) {
2N/A md_perror(dgettext(TEXT_DOMAIN,
2N/A "Could not open device %s"), fname);
2N/A continue;
2N/A }
2N/A
2N/A /* Set ABR state */
2N/A vc.vc_info = 0;
2N/A vc.vc_set = 0;
2N/A if (ioctl(fd, DKIOCGETVOLCAP, &vc) < 0) {
2N/A (void) close(fd);
2N/A continue;
2N/A }
2N/A
2N/A vc.vc_set = DKV_ABR_CAP;
2N/A if (ioctl(fd, DKIOCSETVOLCAP, &vc) < 0) {
2N/A (void) close(fd);
2N/A goto out;
2N/A }
2N/A
2N/A (void) close(fd);
2N/A }
2N/A metafreenamelist(devnlp);
2N/A return (0);
2N/Aout:
2N/A metafreenamelist(devnlp);
2N/A return (-1);
2N/A}
2N/A
2N/A/*
2N/A * FUNCTION: meta_mn_sp_update_abr()
2N/A * INPUT: arg - Given set.
2N/A * PURPOSE: update the ABR state for all soft partitions in the set by
2N/A * forking a process to call meta_sp_update_abr()
2N/A * This function is only called via rpc.metad when adding a node
2N/A * to a set, ie this node is beong joined to the set by another
2N/A * node.
2N/A */
2N/Avoid *
2N/Ameta_mn_sp_update_abr(void *arg)
2N/A{
2N/A set_t setno = *((set_t *)arg);
2N/A mdsetname_t *sp;
2N/A md_error_t mde = mdnullerror;
2N/A int fval;
2N/A
2N/A /* should have a set */
2N/A assert(setno != NULL);
2N/A
2N/A if ((sp = metasetnosetname(setno, &mde)) == NULL) {
2N/A mde_perror(&mde, "");
2N/A return (NULL);
2N/A }
2N/A
2N/A if (!(meta_is_mn_set(sp, &mde))) {
2N/A mde_perror(&mde, "");
2N/A return (NULL);
2N/A }
2N/A
2N/A /* fork a process */
2N/A if ((fval = md_daemonize(sp, &mde)) != 0) {
2N/A /*
2N/A * md_daemonize will fork off a process. The is the
2N/A * parent or error.
2N/A */
2N/A if (fval > 0) {
2N/A return (NULL);
2N/A }
2N/A mde_perror(&mde, "");
2N/A return (NULL);
2N/A }
2N/A /*
2N/A * Child process should never return back to rpc.metad, but
2N/A * should exit.
2N/A * Flush all internally cached data inherited from parent process
2N/A * since cached data will be cleared when parent process RPC request
2N/A * has completed (which is possibly before this child process
2N/A * can complete).
2N/A * Child process can retrieve and cache its own copy of data from
2N/A * rpc.metad that won't be changed by the parent process.
2N/A *
2N/A * Reset md_in_daemon since this child will be a client of rpc.metad
2N/A * not part of the rpc.metad daemon itself.
2N/A * md_in_daemon is used by rpc.metad so that libmeta can tell if
2N/A * this thread is rpc.metad or any other thread. (If this thread
2N/A * was rpc.metad it could use some short circuit code to get data
2N/A * directly from rpc.metad instead of doing an RPC call to rpc.metad).
2N/A */
2N/A md_in_daemon = 0;
2N/A metaflushsetname(sp);
2N/A sr_cache_flush_setno(setno);
2N/A if ((sp = metasetnosetname(setno, &mde)) == NULL) {
2N/A mde_perror(&mde, "");
2N/A md_exit(sp, 1);
2N/A }
2N/A
2N/A
2N/A /*
2N/A * Closing stdin/out/err here.
2N/A */
2N/A (void) close(0);
2N/A (void) close(1);
2N/A (void) close(2);
2N/A assert(fval == 0);
2N/A
2N/A (void) meta_sp_update_abr(sp, &mde);
2N/A
2N/A md_exit(sp, 0);
2N/A /*NOTREACHED*/
2N/A return (NULL);
2N/A}
2N/A
2N/Aint
2N/Ameta_sp_check_component(
2N/A mdsetname_t *sp,
2N/A mdname_t *np,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A md_sp_t *msp;
2N/A minor_t mnum = 0;
2N/A md_dev64_t dev = 0;
2N/A mdnm_params_t nm;
2N/A md_getdevs_params_t mgd;
2N/A side_t sideno;
2N/A char *miscname;
2N/A md_dev64_t *mydev = NULL;
2N/A char *pname = NULL, *t;
2N/A char *ctd_name = NULL;
2N/A char *devname = NULL;
2N/A int len;
2N/A int rval = -1;
2N/A
2N/A (void) memset(&nm, '\0', sizeof (nm));
2N/A if ((msp = meta_get_sp_common(sp, np, 0, ep)) == NULL)
2N/A return (-1);
2N/A
2N/A if ((miscname = metagetmiscname(np, ep)) == NULL)
2N/A return (-1);
2N/A
2N/A sideno = getmyside(sp, ep);
2N/A
2N/A meta_sp_debug("meta_sp_check_component: %s is on %s key: %d"
2N/A " dev: %llu\n",
2N/A np->cname, msp->compnamep->cname, msp->compnamep->key,
2N/A msp->compnamep->dev);
2N/A
2N/A /*
2N/A * Now get the data from the unit structure. The compnamep stuff
2N/A * contains the data from the namespace and we need the un_dev
2N/A * from the unit structure.
2N/A */
2N/A (void) memset(&mgd, '\0', sizeof (mgd));
2N/A MD_SETDRIVERNAME(&mgd, miscname, sp->setno);
2N/A mgd.cnt = 1; /* sp's only have one subdevice */
2N/A mgd.mnum = meta_getminor(np->dev);
2N/A
2N/A mydev = Zalloc(sizeof (*mydev));
2N/A mgd.devs = (uintptr_t)mydev;
2N/A
2N/A if (metaioctl(MD_IOCGET_DEVS, &mgd, &mgd.mde, np->cname) != 0) {
2N/A meta_sp_debug("meta_sp_check_component: ioctl failed\n");
2N/A (void) mdstealerror(ep, &mgd.mde);
2N/A rval = 0;
2N/A goto out;
2N/A } else if (mgd.cnt <= 0) {
2N/A assert(mgd.cnt >= 0);
2N/A rval = 0;
2N/A goto out;
2N/A }
2N/A
2N/A /* Get the devname from the name space. */
2N/A if ((devname = meta_getnmentbykey(sp->setno, sideno,
2N/A msp->compnamep->key, NULL, &mnum, &dev, ep)) == NULL) {
2N/A meta_sp_debug("meta_sp_check_component: key %d not"
2N/A "found\n", msp->compnamep->key);
2N/A goto out;
2N/A }
2N/A
2N/A meta_sp_debug("dev %s from component: (%lu, %lu)\n",
2N/A devname,
2N/A meta_getmajor(*mydev),
2N/A meta_getminor(*mydev));
2N/A meta_sp_debug("minor from the namespace: %lu\n", mnum);
2N/A
2N/A if (mnum != meta_getminor(*mydev)) {
2N/A /*
2N/A * The minor numbers are different. Update the namespace
2N/A * with the information from the component.
2N/A */
2N/A
2N/A t = strrchr(devname, '/');
2N/A t++;
2N/A ctd_name = Strdup(t);
2N/A
2N/A meta_sp_debug("meta_sp_check_component: ctd_name: %s\n",
2N/A ctd_name);
2N/A
2N/A len = strlen(devname);
2N/A t = strrchr(devname, '/');
2N/A t++;
2N/A pname = Zalloc((len - strlen(t)) + 1);
2N/A (void) strncpy(pname, devname, (len - strlen(t)));
2N/A meta_sp_debug("pathname: %s\n", pname);
2N/A
2N/A meta_sp_debug("updating the minor number to %lu\n", nm.mnum);
2N/A
2N/A if (meta_update_namespace(sp->setno, sideno,
2N/A ctd_name, *mydev, msp->compnamep->key, pname,
2N/A ep) != 0) {
2N/A goto out;
2N/A }
2N/A }
2N/Aout:
2N/A if (pname != NULL)
2N/A Free(pname);
2N/A if (ctd_name != NULL)
2N/A Free(ctd_name);
2N/A if (devname != NULL)
2N/A Free(devname);
2N/A if (mydev != NULL)
2N/A Free(mydev);
2N/A return (rval);
2N/A}