OpenSolaris_b135/lib/lvm/libmeta/common/meta_init.c

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#pragma ident	"%Z%%M%	%I%	%E% SMI"

/*
 * initialize metadevices
 */

#include <meta.h>
#include <sys/lvm/mdio.h>
#include <libdevinfo.h>


int
parse_interlace(
	char		*uname,		/* Meta Device name (eg d0) */
	char		*str,		/* String to Parse		 */
	diskaddr_t	*interlacep,
	md_error_t	*ep
)
{
	diskaddr_t	num;
	char		c;
	int		cnt;

	/* parse interlace */
	if ((cnt = sscanf(str, "%llu%c", &num, &c)) < 1) {
		return (meta_cook_syntax(ep, MDE_BAD_INTERLACE,
		    uname, 1, &str));
	} else if (cnt == 1) {
		if (num & (DEV_BSIZE - 1)) {
			return (meta_cook_syntax(ep, MDE_BAD_INTERLACE,
			    uname, 1, &str));
		}
		num = lbtodb(num);
	} else switch (c) {
	case 'b':
	case 'B':
		num *= DEV_BSIZE / DEV_BSIZE;
		break;
	case 'k':
	case 'K':
		num *= 1024 / DEV_BSIZE;
		break;
	case 'm':
	case 'M':
		num *= 1024 * 1024 / DEV_BSIZE;
		break;
	default:
		return (meta_cook_syntax(ep, MDE_BAD_INTERLACE,
		    NULL, 1, &str));
	}

	/* return success */
	*interlacep = num;
	return (0);
}

/*
 * cook up syntax error
 */
int
meta_cook_syntax(
	md_error_t	*ep,
	md_void_errno_t	errcode,
	char		*uname,
	int		argc,
	char		*argv[]
)
{
	int		rval;

	/* if we have a token, concat it to uname */
	if ((argc > 0) && (argv[0] != NULL) && (argv[0][0] != '\0')) {
		char	*p;

		if ((uname != NULL) && (uname[0] != '\0')) {
			p = Malloc(strlen(uname) + 2
			    + 1 + strlen(argv[0]) + 1 + 1);
			(void) strcpy(p, uname);
			(void) strcat(p, ": ");
		} else {
			p = Malloc(1 + strlen(argv[0]) + 1 + 1);
			p[0] = '\0';
		}
		(void) strcat(p, "\"");
		(void) strcat(p, argv[0]);
		(void) strcat(p, "\"");
		rval = mderror(ep, errcode, p);
		Free(p);
	} else {
		rval = mderror(ep, errcode, uname);
	}

	return (rval);
}

int
meta_check_devicesize(
	diskaddr_t	total_blocks
)
{
	int	rval = MD_CRO_32BIT;


	if (total_blocks > MD_MAX_BLKS_FOR_SMALL_DEVS) {
		rval = MD_CRO_64BIT;
	}
	return (rval);
}


/*
 * setup metadevice geometry
 */
/*ARGSUSED*/
int
meta_setup_geom(
	md_unit_t	*md,
	mdname_t	*np,
	mdgeom_t	*geomp,
	uint_t		write_reinstruct,
	uint_t		read_reinstruct,
	uint_t		round_cyl,
	md_error_t	*ep
)
{
	diskaddr_t	cylsize = geomp->nhead * geomp->nsect;
	diskaddr_t	total_blocks;

	if (round_cyl) {
		total_blocks = rounddown(md->c.un_actual_tb, cylsize);
	} else {
		total_blocks = md->c.un_actual_tb;
	}

	md->c.un_total_blocks = total_blocks;
	md->c.un_nhead = geomp->nhead;
	md->c.un_nsect = geomp->nsect;
	md->c.un_rpm = geomp->rpm;
	md->c.un_wr_reinstruct = write_reinstruct;
	md->c.un_rd_reinstruct = read_reinstruct;
	return (0);
}

/*
 * adjust metadevice geometry
 */
/*ARGSUSED*/
int
meta_adjust_geom(
	md_unit_t	*md,
	mdname_t	*np,
	uint_t		write_reinstruct,
	uint_t		read_reinstruct,
	uint_t		round_cyl,
	md_error_t	*ep
)
{
	diskaddr_t	cylsize = md->c.un_nhead * md->c.un_nsect;
	diskaddr_t	total_blocks;

	if (round_cyl) {
		total_blocks = rounddown(md->c.un_actual_tb, cylsize);
	} else {
		total_blocks = md->c.un_actual_tb;
	}

	md->c.un_total_blocks = total_blocks;
	if (write_reinstruct > md->c.un_wr_reinstruct)
		md->c.un_wr_reinstruct = write_reinstruct;
	if (read_reinstruct > md->c.un_rd_reinstruct)
		md->c.un_rd_reinstruct = read_reinstruct;
	return (0);
}

/*
 * Function: meta_init_make_device
 * Purpose:
 * 	Create the device node <uname> by constructing the necessary
 * 	md_mkdev_params_t structure. We have to handle relative names
 *	(e.g. "d80") and fully-qualified names (e.g. "/dev/md/red/dsk/d80").
 *	The field that we need is the unit number of the metadevice (80 in
 *	the above examples).
 * Input:	spp	set structure
 *		uname	unit-name (fully qualified or relative)
 * Output:	ep	error return structure
 * Returns:	> 0	success and return 'key'
 *		-1	Error. <ep> contains error reason
 */
mdkey_t
meta_init_make_device(
	mdsetname_t	**spp,
	char		*uname,
	md_error_t	*ep
)
{
	md_mkdev_params_t	params;
	mdkey_t			rval = 0;
	char			*p;
	int			len = strlen(uname);

	(void) memset(&params, 0, sizeof (params));
	MD_SETDRIVERNAME(&params, "md", (*spp)->setno);

	/*
	 * This ioctl call causes kernel to allocate a unit number
	 * and populate /devices for the named metadevice
	 */
	if (metaioctl(MD_IOCMAKE_DEV, &params, &params.mde, NULL) != 0) {
		return (mdstealerror(ep, &params.mde));
	}

	/*
	 * Now we have minor number so add it to the namespace
	 * and return the key
	 */
	if ((rval = add_self_name(*spp, uname, &params, ep)) <= 0) {
		if (mdisok(ep))
			(void) mderror(ep, MDE_UNIT_NOT_FOUND, NULL);

		return (-1);
	}

	/* Make sure the /dev link is created */
	if (meta_update_devtree(MD_MKMIN((*spp)->setno, params.un)) != 0) {
		/*
		 * Delete name entry we just created
		 */
		(void) del_self_name(*spp, rval, ep);
		p = Malloc(len + 3);
		(void) snprintf(p, len + 3, "\"%s\"", uname);
		rval = mderror(ep, MDE_UNIT_NOT_FOUND, p);
		Free(p);
	}
	return (rval);
}

/*
 * FUNCTION:	is_metadb_cmd()
 * INPUT:	argc	- number of command line arguments
 *		argv	- pointer to array of command line arguments
 * OUTPUT:	none
 * RETURNS:	TRUE if a metadb is to be created, FALSE otherwise
 * PURPOSE:	parses enough of the command line to determine if a metadb
 *		create is being attempted
 */
static boolean_t
is_metadb_cmd(
	int	argc,
	char	*argv[]
)
{
	ulong_t	num;
	int	len;

	/* look for match */
	if (argc > 0 && (sscanf(argv[0], "mddb%lu%n", &num, &len) == 1) &&
		    (strlen(argv[0]) == len) && ((long)num >= 0)) {
		return (B_TRUE);
	}

	return (B_FALSE);
}

/*
 * FUNCTION:	is_stripe_cmd()
 * INPUT:	argc	- number of command line arguments
 *		argv	- pointer to array of command line arguments
 * OUTPUT:	none
 * RETURNS:	TRUE if a stripe is to be created, FALSE otherwise
 * PURPOSE:	parses enough of the command line to determine if a stripe
 *		create is being attempted
 */
static boolean_t
is_stripe_cmd(
	int	argc,
	char	*argv[]
)
{
	uint_t	nrow;

	if (argc > 1 && (sscanf(argv[1], "%u", &nrow) != 1) || ((int)nrow < 0))
		return (B_FALSE);

	return (B_TRUE);
}

/*
 * FUNCTION:	meta_get_init_type()
 * INPUT:	argc	- number of command line arguments
 *		argv	- pointer to array of command line arguments
 * OUTPUT:	none
 * RETURNS:	type of metadevice or hot spare pools being initialized
 * PURPOSE:	parses enough of the command line to determine what type
 *		of metainit is being attempted
 */
mdinittypes_t
meta_get_init_type(
	int 	argc,
	char	*argv[]
)
{
	char		*arg = argv[1];
	mdinittypes_t	init_type;

	if (argc == 1) /* must be a hot spare pool w/o devices */
		return (TAB_HSP);

	init_type = TAB_UNKNOWN;
	if (arg != NULL) {
		if (strcmp(arg, "-m") == 0) {
			init_type = TAB_MIRROR;
		} else if (strcmp(arg, "-r") == 0) {
			init_type = TAB_RAID;
		} else if (strcmp(arg, "-p") == 0) {
			init_type = TAB_SP;
		} else if (strcmp(arg, "-t") == 0) {
			init_type = TAB_TRANS;
		} else if (is_metadb_cmd(argc, argv)) {
			init_type = TAB_MDDB;
		} else if (is_stripe_cmd(argc, argv)) {
			init_type = TAB_STRIPE;
		} else { /* assume that it is a hsp */
			init_type = TAB_HSP;
		}
	}
	return (init_type);
}

/*
 * initialize named device or hotspare pool
 */
int
meta_init_name(
	mdsetname_t	**spp,
	int		argc,
	char		*argv[],
	char		*cname, /* canonical name */
	mdcmdopts_t	options,
	md_error_t	*ep
)
{
	mdinittypes_t	init_type;
	char		*p;
	int		rval;
	char		*uname = argv[0];
	mdkey_t		key = MD_KEYWILD;
	minor_t		mnum;
	md_error_t	t_e = mdnullerror;

	assert(argc > 0);
	assert(*spp != NULL);

	/* determine type of metadevice or hot spare pool being created */
	init_type = meta_get_init_type(argc, argv);

	/*
	 * Metatrans is eof
	 */
	if (init_type == TAB_TRANS)
		return (mderror(ep, MDE_EOF_TRANS, NULL));

	/* hotspare pool */
	if (init_type == TAB_HSP)
		return (meta_init_hsp(spp, argc, argv, options, ep));

	/*
	 * We are creating metadevice so make sure the name
	 * has not been used
	 */
	if (is_existing_meta_hsp(*spp, cname)) {
		/*
		 * The name has been used by hsp
		 */
		if (is_existing_hsp(*spp, cname)) {
			return (mderror(ep, MDE_NAME_IN_USE, cname));
		}

		/*
		 * If path exists but unit is not created
		 * then meta_init_make_device will correct
		 * that.  If unit also exists then it
		 * will return a conflict error
		 */
		if (init_type != TAB_UNKNOWN) {
		    /* Create device node */
		    if ((key = meta_init_make_device(spp, uname,
			&t_e)) <= 0) {
			return (mdstealerror(ep, &t_e));
		    }
		}
	}

	/* metadevice */
	if (argc >= 2 && init_type != TAB_UNKNOWN) {
		/*
		 * We need to create the device node if the specified metadevice
		 * does not already exist in the database. The actual creation
		 * is undertaken by the md driver and the links propagated by
		 * devfsadm.
		 */
		if (key == MD_KEYWILD) {
			if ((key = meta_init_make_device(spp, uname,
			    &t_e)) <= 0)
				return (mdstealerror(ep, &t_e));
		}

		switch (init_type) {
		case TAB_MIRROR:
			rval = meta_init_mirror(spp, argc, argv, options, ep);
			break;
		case TAB_RAID:
			rval = meta_init_raid(spp, argc, argv, options, ep);
			break;
		case TAB_SP:
			rval = meta_init_sp(spp, argc, argv, options, ep);
			break;
		case TAB_STRIPE:
			rval = meta_init_stripe(spp, argc, argv, options, ep);
			break;
		}

		if (rval == -1 || !(options & MDCMD_DOIT)) {
			/*
			 * Remove the device node created before
			 */
			if ((meta_getnmentbykey((*spp)->setno, MD_SIDEWILD,
			    key, NULL, &mnum, NULL, ep) != NULL) &&
			    MD_MIN2UNIT(mnum) < MD_MAXUNITS) {
			    (void) metaioctl(MD_IOCREM_DEV, &mnum, &t_e, NULL);
			}

			/*
			 * Del what we added before
			 */
			(void) del_self_name(*spp, key, &t_e);
		}
		return (rval);
	}

	/* unknown type */
	p = Malloc(1 + strlen(uname) + 1 + 1);
	(void) strcpy(p, "\"");
	(void) strcat(p, uname);
	(void) strcat(p, "\"");
	rval = mderror(ep, MDE_SYNTAX, p);
	Free(p);
	return (rval);
}