diff options
Diffstat (limited to 'usr/src/lib/libipd/common/libipd.c')
| -rw-r--r-- | usr/src/lib/libipd/common/libipd.c | 303 | 
1 files changed, 303 insertions, 0 deletions
| diff --git a/usr/src/lib/libipd/common/libipd.c b/usr/src/lib/libipd/common/libipd.c new file mode 100644 index 0000000000..c580fdf61e --- /dev/null +++ b/usr/src/lib/libipd/common/libipd.c @@ -0,0 +1,303 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source.  A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2012 Joyent, Inc.  All rights reserved. + * Use is subject to license terms. + */ + +#include <sys/types.h> +#include <stdio.h> +#include <fcntl.h> +#include <errno.h> +#include <string.h> +#include <strings.h> +#include <stdlib.h> +#include <unistd.h> +#include <stdarg.h> + +#include <libipd.h> +#include <sys/ipd.h> + +__thread ipd_errno_t ipd_errno = 0; +__thread char ipd_errmsg[512]; + +struct ipd_stat { +	uint_t is_nzones; +	zoneid_t *is_zoneids; +	struct ipd_config *is_configs; +}; + +static ipd_errno_t +xlate_errno(int e) +{ +	switch (e) { +	case 0: +		return (EIPD_NOERROR); +	case ENOMEM: +	case EAGAIN: +		return (EIPD_NOMEM); +	case ERANGE: +		return (EIPD_RANGE); +	case EPERM: +		return (EIPD_PERM); +	case EFAULT: +		return (EIPD_FAULT); +	case ENOTTY: +		return (EIPD_INTERNAL); +	default: +		return (EIPD_UNKNOWN); +	} +} + +const char * +ipd_strerror(ipd_errno_t e) +{ +	switch (e) { +	case EIPD_NOERROR: +		return ("no error"); +	case EIPD_NOMEM: +		return ("out of memory"); +	case EIPD_ZC_NOENT: +		return ("zone does not exist or is not using ipd"); +	case EIPD_RANGE: +		return ("argument out of range"); +	case EIPD_PERM: +		return ("permission denied"); +	case EIPD_FAULT: +		return ("bad pointer"); +	case EIPD_INTERNAL: +		return ("internal library error"); +	case EIPD_UNKNOWN: +	default: +		return ("unknown error"); +	} +} + +static int +ipd_set_errno(ipd_errno_t e, const char *fmt, ...) +{ +	va_list ap; + +	ipd_errno = e; +	if (fmt != NULL) { +		va_start(ap, fmt); +		(void) vsnprintf(ipd_errmsg, sizeof (ipd_errmsg), fmt, ap); +		va_end(ap); +	} else { +		(void) strlcpy(ipd_errmsg, +		    ipd_strerror(e), sizeof (ipd_errmsg)); +	} + +	return (-1); +} + +int +ipd_open(const char *path) +{ +	int fd; + +	if (path == NULL) +		path = IPD_DEV_PATH; + +	fd = open(path, O_RDWR); +	if (fd < 0) { +		return (ipd_set_errno(xlate_errno(errno), +		    "unable to open %s: %s", path, strerror(errno))); +	} + +	return (fd); +} + +int +ipd_close(int fd) +{ +	(void) close(fd); +	return (0); +} + +int +ipd_status_read(int fd, ipd_stathdl_t *ispp) +{ +	struct ipd_stat *isp = NULL; +	ipd_ioc_list_t ipil; +	uint_t rzones; +	uint_t i; + +	bzero(&ipil, sizeof (ipil)); +	if (ioctl(fd, IPDIOC_LIST, &ipil) != 0) { +		return (ipd_set_errno(xlate_errno(errno), +		    "unable to retrieve ipd zone list: %s", strerror(errno))); +	} + +	for (;;) { +		if ((rzones = ipil.ipil_nzones) == 0) +			break; + +		ipil.ipil_info = +		    malloc(sizeof (ipd_ioc_info_t) * ipil.ipil_nzones); +		if (ipil.ipil_info == NULL) +			return (ipd_set_errno(EIPD_NOMEM, NULL)); + +		if (ioctl(fd, IPDIOC_LIST, &ipil) != 0) { +			free(ipil.ipil_info); +			return (ipd_set_errno(xlate_errno(errno), +			    "unable to retrieve ipd zone list: %s", +			    strerror(errno))); +		} + +		if (ipil.ipil_nzones <= rzones) +			break; + +		free(ipil.ipil_info); +	} + +	if ((isp = malloc(sizeof (struct ipd_stat))) == NULL) { +		free(ipil.ipil_info); +		return (ipd_set_errno(EIPD_NOMEM, NULL)); +	} + +	isp->is_nzones = ipil.ipil_nzones; + +	if (isp->is_nzones == 0) { +		isp->is_zoneids = NULL; +		isp->is_configs = NULL; +		*ispp = isp; +		return (0); +	} + +	isp->is_zoneids = malloc(sizeof (zoneid_t) * ipil.ipil_nzones); +	if (isp->is_zoneids == NULL) { +		free(ipil.ipil_info); +		free(isp); +		return (ipd_set_errno(EIPD_NOMEM, NULL)); +	} +	isp->is_configs = malloc(sizeof (struct ipd_config) * ipil.ipil_nzones); +	if (isp->is_configs == NULL) { +		free(ipil.ipil_info); +		free(isp->is_zoneids); +		free(isp); +		return (ipd_set_errno(EIPD_NOMEM, NULL)); +	} + +	for (i = 0; i < isp->is_nzones; i++) { +		isp->is_zoneids[i] = ipil.ipil_info[i].ipii_zoneid; + +		isp->is_configs[i].ic_corrupt = ipil.ipil_info[i].ipii_corrupt; +		isp->is_configs[i].ic_drop = ipil.ipil_info[i].ipii_drop; +		isp->is_configs[i].ic_delay = ipil.ipil_info[i].ipii_delay; + +		isp->is_configs[i].ic_mask = +		    ((!!isp->is_configs[i].ic_corrupt) * IPDM_CORRUPT) | +		    ((!!isp->is_configs[i].ic_drop) * IPDM_DROP) | +		    ((!!isp->is_configs[i].ic_delay) * IPDM_DELAY); +	} + +	*ispp = isp; +	return (0); +} + +void +ipd_status_foreach_zone(const ipd_stathdl_t hdl, ipd_status_cb_f f, void *arg) +{ +	const struct ipd_stat *isp = hdl; +	uint_t i; + +	for (i = 0; i < isp->is_nzones; i++) +		f(isp->is_zoneids[i], &isp->is_configs[i], arg); +} + +int +ipd_status_get_config(const ipd_stathdl_t hdl, zoneid_t z, ipd_config_t **icpp) +{ +	const struct ipd_stat *isp = hdl; +	uint_t i; + +	for (i = 0; i < isp->is_nzones; i++) { +		if (isp->is_zoneids[i] == z) { +			*icpp = &isp->is_configs[i]; +			return (0); +		} +	} + +	return (ipd_set_errno(EIPD_ZC_NOENT, +	    "zone %d does not exist or has no ipd configuration", z)); +} + +void +ipd_status_free(ipd_stathdl_t hdl) +{ +	struct ipd_stat *isp = hdl; + +	if (isp != NULL) { +		free(isp->is_zoneids); +		free(isp->is_configs); +	} +	free(isp); +} + +int +ipd_ctl(int fd, zoneid_t z, const ipd_config_t *icp) +{ +	ipd_ioc_perturb_t ipip; + +	bzero(&ipip, sizeof (ipd_ioc_perturb_t)); +	ipip.ipip_zoneid = z; + +	if (icp->ic_mask & IPDM_CORRUPT) { +		if (icp->ic_corrupt == 0) +			ipip.ipip_arg |= IPD_CORRUPT; +	} +	if (icp->ic_mask & IPDM_DELAY) { +		if (icp->ic_delay == 0) +			ipip.ipip_arg |= IPD_DELAY; +	} +	if (icp->ic_mask & IPDM_DROP) { +		if (icp->ic_drop == 0) +			ipip.ipip_arg |= IPD_DROP; +	} + +	if (ipip.ipip_arg != 0 && ioctl(fd, IPDIOC_REMOVE, &ipip) != 0) { +		return (ipd_set_errno(xlate_errno(errno), +		    "unable to remove cleared ipd settings: %s", +		    strerror(errno))); +	} + +	if ((icp->ic_mask & IPDM_CORRUPT) && icp->ic_corrupt != 0) { +		ipip.ipip_zoneid = z; +		ipip.ipip_arg = icp->ic_corrupt; +		if (ioctl(fd, IPDIOC_CORRUPT, &ipip) != 0) { +			return (ipd_set_errno(xlate_errno(errno), +			    "unable to set corruption rate to %d: %s", +			    ipip.ipip_arg, strerror(errno))); +		} +	} +	if ((icp->ic_mask & IPDM_DELAY) && icp->ic_delay != 0) { +		ipip.ipip_zoneid = z; +		ipip.ipip_arg = icp->ic_delay; +		if (ioctl(fd, IPDIOC_DELAY, &ipip) != 0) { +			return (ipd_set_errno(xlate_errno(errno), +			    "unable to set delay time to %d: %s", +			    ipip.ipip_arg, strerror(errno))); +		} +	} +	if ((icp->ic_mask & IPDM_DROP) && icp->ic_drop != 0) { +		ipip.ipip_zoneid = z; +		ipip.ipip_arg = icp->ic_drop; +		if (ioctl(fd, IPDIOC_DROP, &ipip) != 0) { +			return (ipd_set_errno(xlate_errno(errno), +			    "unable to set drop probability to %d: %s", +			    ipip.ipip_arg, strerror(errno))); +		} +	} + +	return (0); +} | 
