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 2006 Sun Microsystems, Inc. All rights reserved.
2N/A * Use is subject to license terms.
2N/A */
2N/A
2N/A#pragma ident "%Z%%M% %I% %E% SMI"
2N/A
2N/A/*
2N/A * initialize metadevices
2N/A */
2N/A
2N/A#include <meta.h>
2N/A#include <sys/lvm/mdio.h>
2N/A#include <libdevinfo.h>
2N/A
2N/A
2N/Aint
2N/Aparse_interlace(
2N/A char *uname, /* Meta Device name (eg d0) */
2N/A char *str, /* String to Parse */
2N/A diskaddr_t *interlacep,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A diskaddr_t num;
2N/A char c;
2N/A int cnt;
2N/A
2N/A /* parse interlace */
2N/A if ((cnt = sscanf(str, "%llu%c", &num, &c)) < 1) {
2N/A return (meta_cook_syntax(ep, MDE_BAD_INTERLACE,
2N/A uname, 1, &str));
2N/A } else if (cnt == 1) {
2N/A if (num & (DEV_BSIZE - 1)) {
2N/A return (meta_cook_syntax(ep, MDE_BAD_INTERLACE,
2N/A uname, 1, &str));
2N/A }
2N/A num = lbtodb(num);
2N/A } else switch (c) {
2N/A case 'b':
2N/A case 'B':
2N/A num *= DEV_BSIZE / DEV_BSIZE;
2N/A break;
2N/A case 'k':
2N/A case 'K':
2N/A num *= 1024 / DEV_BSIZE;
2N/A break;
2N/A case 'm':
2N/A case 'M':
2N/A num *= 1024 * 1024 / DEV_BSIZE;
2N/A break;
2N/A default:
2N/A return (meta_cook_syntax(ep, MDE_BAD_INTERLACE,
2N/A NULL, 1, &str));
2N/A }
2N/A
2N/A /* return success */
2N/A *interlacep = num;
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * cook up syntax error
2N/A */
2N/Aint
2N/Ameta_cook_syntax(
2N/A md_error_t *ep,
2N/A md_void_errno_t errcode,
2N/A char *uname,
2N/A int argc,
2N/A char *argv[]
2N/A)
2N/A{
2N/A int rval;
2N/A
2N/A /* if we have a token, concat it to uname */
2N/A if ((argc > 0) && (argv[0] != NULL) && (argv[0][0] != '\0')) {
2N/A char *p;
2N/A
2N/A if ((uname != NULL) && (uname[0] != '\0')) {
2N/A p = Malloc(strlen(uname) + 2
2N/A + 1 + strlen(argv[0]) + 1 + 1);
2N/A (void) strcpy(p, uname);
2N/A (void) strcat(p, ": ");
2N/A } else {
2N/A p = Malloc(1 + strlen(argv[0]) + 1 + 1);
2N/A p[0] = '\0';
2N/A }
2N/A (void) strcat(p, "\"");
2N/A (void) strcat(p, argv[0]);
2N/A (void) strcat(p, "\"");
2N/A rval = mderror(ep, errcode, p);
2N/A Free(p);
2N/A } else {
2N/A rval = mderror(ep, errcode, uname);
2N/A }
2N/A
2N/A return (rval);
2N/A}
2N/A
2N/Aint
2N/Ameta_check_devicesize(
2N/A diskaddr_t total_blocks
2N/A)
2N/A{
2N/A int rval = MD_CRO_32BIT;
2N/A
2N/A
2N/A if (total_blocks > MD_MAX_BLKS_FOR_SMALL_DEVS) {
2N/A rval = MD_CRO_64BIT;
2N/A }
2N/A return (rval);
2N/A}
2N/A
2N/A
2N/A/*
2N/A * setup metadevice geometry
2N/A */
2N/A/*ARGSUSED*/
2N/Aint
2N/Ameta_setup_geom(
2N/A md_unit_t *md,
2N/A mdname_t *np,
2N/A mdgeom_t *geomp,
2N/A uint_t write_reinstruct,
2N/A uint_t read_reinstruct,
2N/A uint_t round_cyl,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A diskaddr_t cylsize = geomp->nhead * geomp->nsect;
2N/A diskaddr_t total_blocks;
2N/A
2N/A if (round_cyl) {
2N/A total_blocks = rounddown(md->c.un_actual_tb, cylsize);
2N/A } else {
2N/A total_blocks = md->c.un_actual_tb;
2N/A }
2N/A
2N/A md->c.un_total_blocks = total_blocks;
2N/A md->c.un_nhead = geomp->nhead;
2N/A md->c.un_nsect = geomp->nsect;
2N/A md->c.un_rpm = geomp->rpm;
2N/A md->c.un_wr_reinstruct = write_reinstruct;
2N/A md->c.un_rd_reinstruct = read_reinstruct;
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * adjust metadevice geometry
2N/A */
2N/A/*ARGSUSED*/
2N/Aint
2N/Ameta_adjust_geom(
2N/A md_unit_t *md,
2N/A mdname_t *np,
2N/A uint_t write_reinstruct,
2N/A uint_t read_reinstruct,
2N/A uint_t round_cyl,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A diskaddr_t cylsize = md->c.un_nhead * md->c.un_nsect;
2N/A diskaddr_t total_blocks;
2N/A
2N/A if (round_cyl) {
2N/A total_blocks = rounddown(md->c.un_actual_tb, cylsize);
2N/A } else {
2N/A total_blocks = md->c.un_actual_tb;
2N/A }
2N/A
2N/A md->c.un_total_blocks = total_blocks;
2N/A if (write_reinstruct > md->c.un_wr_reinstruct)
2N/A md->c.un_wr_reinstruct = write_reinstruct;
2N/A if (read_reinstruct > md->c.un_rd_reinstruct)
2N/A md->c.un_rd_reinstruct = read_reinstruct;
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * Function: meta_init_make_device
2N/A * Purpose:
2N/A * Create the device node <uname> by constructing the necessary
2N/A * md_mkdev_params_t structure. We have to handle relative names
2N/A * (e.g. "d80") and fully-qualified names (e.g. "/dev/md/red/dsk/d80").
2N/A * The field that we need is the unit number of the metadevice (80 in
2N/A * the above examples).
2N/A * Input: spp set structure
2N/A * uname unit-name (fully qualified or relative)
2N/A * Output: ep error return structure
2N/A * Returns: > 0 success and return 'key'
2N/A * -1 Error. <ep> contains error reason
2N/A */
2N/Amdkey_t
2N/Ameta_init_make_device(
2N/A mdsetname_t **spp,
2N/A char *uname,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A md_mkdev_params_t params;
2N/A mdkey_t rval = 0;
2N/A char *p;
2N/A int len = strlen(uname);
2N/A
2N/A (void) memset(&params, 0, sizeof (params));
2N/A MD_SETDRIVERNAME(&params, "md", (*spp)->setno);
2N/A
2N/A /*
2N/A * This ioctl call causes kernel to allocate a unit number
2N/A * and populate /devices for the named metadevice
2N/A */
2N/A if (metaioctl(MD_IOCMAKE_DEV, &params, &params.mde, NULL) != 0) {
2N/A return (mdstealerror(ep, &params.mde));
2N/A }
2N/A
2N/A /*
2N/A * Now we have minor number so add it to the namespace
2N/A * and return the key
2N/A */
2N/A if ((rval = add_self_name(*spp, uname, &params, ep)) <= 0) {
2N/A if (mdisok(ep))
2N/A (void) mderror(ep, MDE_UNIT_NOT_FOUND, NULL);
2N/A
2N/A return (-1);
2N/A }
2N/A
2N/A /* Make sure the /dev link is created */
2N/A if (meta_update_devtree(MD_MKMIN((*spp)->setno, params.un)) != 0) {
2N/A /*
2N/A * Delete name entry we just created
2N/A */
2N/A (void) del_self_name(*spp, rval, ep);
2N/A p = Malloc(len + 3);
2N/A (void) snprintf(p, len + 3, "\"%s\"", uname);
2N/A rval = mderror(ep, MDE_UNIT_NOT_FOUND, p);
2N/A Free(p);
2N/A }
2N/A return (rval);
2N/A}
2N/A
2N/A/*
2N/A * FUNCTION: is_metadb_cmd()
2N/A * INPUT: argc - number of command line arguments
2N/A * argv - pointer to array of command line arguments
2N/A * OUTPUT: none
2N/A * RETURNS: TRUE if a metadb is to be created, FALSE otherwise
2N/A * PURPOSE: parses enough of the command line to determine if a metadb
2N/A * create is being attempted
2N/A */
2N/Astatic boolean_t
2N/Ais_metadb_cmd(
2N/A int argc,
2N/A char *argv[]
2N/A)
2N/A{
2N/A ulong_t num;
2N/A int len;
2N/A
2N/A /* look for match */
2N/A if (argc > 0 && (sscanf(argv[0], "mddb%lu%n", &num, &len) == 1) &&
2N/A (strlen(argv[0]) == len) && ((long)num >= 0)) {
2N/A return (B_TRUE);
2N/A }
2N/A
2N/A return (B_FALSE);
2N/A}
2N/A
2N/A/*
2N/A * FUNCTION: is_stripe_cmd()
2N/A * INPUT: argc - number of command line arguments
2N/A * argv - pointer to array of command line arguments
2N/A * OUTPUT: none
2N/A * RETURNS: TRUE if a stripe is to be created, FALSE otherwise
2N/A * PURPOSE: parses enough of the command line to determine if a stripe
2N/A * create is being attempted
2N/A */
2N/Astatic boolean_t
2N/Ais_stripe_cmd(
2N/A int argc,
2N/A char *argv[]
2N/A)
2N/A{
2N/A uint_t nrow;
2N/A
2N/A if (argc > 1 && (sscanf(argv[1], "%u", &nrow) != 1) || ((int)nrow < 0))
2N/A return (B_FALSE);
2N/A
2N/A return (B_TRUE);
2N/A}
2N/A
2N/A/*
2N/A * FUNCTION: meta_get_init_type()
2N/A * INPUT: argc - number of command line arguments
2N/A * argv - pointer to array of command line arguments
2N/A * OUTPUT: none
2N/A * RETURNS: type of metadevice or hot spare pools being initialized
2N/A * PURPOSE: parses enough of the command line to determine what type
2N/A * of metainit is being attempted
2N/A */
2N/Amdinittypes_t
2N/Ameta_get_init_type(
2N/A int argc,
2N/A char *argv[]
2N/A)
2N/A{
2N/A char *arg = argv[1];
2N/A mdinittypes_t init_type;
2N/A
2N/A if (argc == 1) /* must be a hot spare pool w/o devices */
2N/A return (TAB_HSP);
2N/A
2N/A init_type = TAB_UNKNOWN;
2N/A if (arg != NULL) {
2N/A if (strcmp(arg, "-m") == 0) {
2N/A init_type = TAB_MIRROR;
2N/A } else if (strcmp(arg, "-r") == 0) {
2N/A init_type = TAB_RAID;
2N/A } else if (strcmp(arg, "-p") == 0) {
2N/A init_type = TAB_SP;
2N/A } else if (strcmp(arg, "-t") == 0) {
2N/A init_type = TAB_TRANS;
2N/A } else if (is_metadb_cmd(argc, argv)) {
2N/A init_type = TAB_MDDB;
2N/A } else if (is_stripe_cmd(argc, argv)) {
2N/A init_type = TAB_STRIPE;
2N/A } else { /* assume that it is a hsp */
2N/A init_type = TAB_HSP;
2N/A }
2N/A }
2N/A return (init_type);
2N/A}
2N/A
2N/A/*
2N/A * initialize named device or hotspare pool
2N/A */
2N/Aint
2N/Ameta_init_name(
2N/A mdsetname_t **spp,
2N/A int argc,
2N/A char *argv[],
2N/A char *cname, /* canonical name */
2N/A mdcmdopts_t options,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A mdinittypes_t init_type;
2N/A char *p;
2N/A int rval;
2N/A char *uname = argv[0];
2N/A mdkey_t key = MD_KEYWILD;
2N/A minor_t mnum;
2N/A md_error_t t_e = mdnullerror;
2N/A
2N/A assert(argc > 0);
2N/A assert(*spp != NULL);
2N/A
2N/A /* determine type of metadevice or hot spare pool being created */
2N/A init_type = meta_get_init_type(argc, argv);
2N/A
2N/A /*
2N/A * Metatrans is eof
2N/A */
2N/A if (init_type == TAB_TRANS)
2N/A return (mderror(ep, MDE_EOF_TRANS, NULL));
2N/A
2N/A /* hotspare pool */
2N/A if (init_type == TAB_HSP)
2N/A return (meta_init_hsp(spp, argc, argv, options, ep));
2N/A
2N/A /*
2N/A * We are creating metadevice so make sure the name
2N/A * has not been used
2N/A */
2N/A if (is_existing_meta_hsp(*spp, cname)) {
2N/A /*
2N/A * The name has been used by hsp
2N/A */
2N/A if (is_existing_hsp(*spp, cname)) {
2N/A return (mderror(ep, MDE_NAME_IN_USE, cname));
2N/A }
2N/A
2N/A /*
2N/A * If path exists but unit is not created
2N/A * then meta_init_make_device will correct
2N/A * that. If unit also exists then it
2N/A * will return a conflict error
2N/A */
2N/A if (init_type != TAB_UNKNOWN) {
2N/A /* Create device node */
2N/A if ((key = meta_init_make_device(spp, uname,
2N/A &t_e)) <= 0) {
2N/A return (mdstealerror(ep, &t_e));
2N/A }
2N/A }
2N/A }
2N/A
2N/A /* metadevice */
2N/A if (argc >= 2 && init_type != TAB_UNKNOWN) {
2N/A /*
2N/A * We need to create the device node if the specified metadevice
2N/A * does not already exist in the database. The actual creation
2N/A * is undertaken by the md driver and the links propagated by
2N/A * devfsadm.
2N/A */
2N/A if (key == MD_KEYWILD) {
2N/A if ((key = meta_init_make_device(spp, uname,
2N/A &t_e)) <= 0)
2N/A return (mdstealerror(ep, &t_e));
2N/A }
2N/A
2N/A switch (init_type) {
2N/A case TAB_MIRROR:
2N/A rval = meta_init_mirror(spp, argc, argv, options, ep);
2N/A break;
2N/A case TAB_RAID:
2N/A rval = meta_init_raid(spp, argc, argv, options, ep);
2N/A break;
2N/A case TAB_SP:
2N/A rval = meta_init_sp(spp, argc, argv, options, ep);
2N/A break;
2N/A case TAB_STRIPE:
2N/A rval = meta_init_stripe(spp, argc, argv, options, ep);
2N/A break;
2N/A }
2N/A
2N/A if (rval == -1 || !(options & MDCMD_DOIT)) {
2N/A /*
2N/A * Remove the device node created before
2N/A */
2N/A if ((meta_getnmentbykey((*spp)->setno, MD_SIDEWILD,
2N/A key, NULL, &mnum, NULL, ep) != NULL) &&
2N/A MD_MIN2UNIT(mnum) < MD_MAXUNITS) {
2N/A (void) metaioctl(MD_IOCREM_DEV, &mnum, &t_e, NULL);
2N/A }
2N/A
2N/A /*
2N/A * Del what we added before
2N/A */
2N/A (void) del_self_name(*spp, key, &t_e);
2N/A }
2N/A return (rval);
2N/A }
2N/A
2N/A /* unknown type */
2N/A p = Malloc(1 + strlen(uname) + 1 + 1);
2N/A (void) strcpy(p, "\"");
2N/A (void) strcat(p, uname);
2N/A (void) strcat(p, "\"");
2N/A rval = mderror(ep, MDE_SYNTAX, p);
2N/A Free(p);
2N/A return (rval);
2N/A}