Home | History | Annotate | Download | only in etm
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 
     22 /*
     23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
     24  * Use is subject to license terms.
     25  */
     26 
     27 /*
     28  * etm_xport_api_dd.c	FMA ETM-to-Transport API implementation
     29  *			for sun4v/Ontario
     30  *
     31  * library for establishing connections and transporting FMA events
     32  * between ETMs (event transport modules) in separate fault domain,
     33  * ie, between domain and service processor in same chassis, using
     34  * a character device driver based transport
     35  */
     36 
     37 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     38 
     39 /*
     40  * --------------------------------- includes --------------------------------
     41  */
     42 
     43 #include <sys/types.h>
     44 #include <sys/stat.h>
     45 #include <sys/fm/protocol.h>
     46 #include <fm/fmd_api.h>
     47 
     48 #include <pthread.h>
     49 #include <stdio.h>
     50 #include <stropts.h>
     51 #include <locale.h>
     52 #include <strings.h>
     53 #include <stdlib.h>
     54 #include <unistd.h>
     55 #include <limits.h>
     56 #include <alloca.h>
     57 #include <errno.h>
     58 #include <fcntl.h>
     59 #include <time.h>
     60 #include <poll.h>
     61 #include <sys/ldc.h>
     62 #include <sys/vldc.h>
     63 
     64 #include "etm_xport_api.h"
     65 #include "etm_etm_proto.h"
     66 #include "etm_impl.h"
     67 
     68 /*
     69  * ----------------------- private consts and defns --------------------------
     70  */
     71 
     72 /* magic numbers (32 bits) for transport address and connection handle */
     73 
     74 #define	ETM_XPORT_DD_MAGIC_ADDR	(0x45544D41)
     75 #define	ETM_XPORT_DD_MAGIC_CONN	(0x45544D43)
     76 
     77 /* flags to use in opening transport device */
     78 
     79 #define	ETM_XPORT_OPEN_FLAGS		(O_RDWR | O_NOCTTY)
     80 
     81 /*
     82  * transport address and connection handle structures overload fn and fd
     83  * fields to include state information:
     84  *
     85  *	fn	file name		NULL means unused or closed
     86  *	fd	file descriptor		-1 means unused or closed
     87  */
     88 
     89 typedef struct _etm_xport_addr {
     90 	uint32_t		magic_num;	/* magic number */
     91 	char			*fn;		/* fullpath to device node */
     92 } _etm_xport_addr_t;
     93 
     94 typedef struct _etm_xport_conn {
     95 	uint32_t		magic_num;	/* magic number */
     96 	int			fd;		/* open dev file descriptor */
     97 	_etm_xport_addr_t	*addr;		/* associated transport addr */
     98 } _etm_xport_conn_t;
     99 
    100 /*
    101  * filename of device node to reach SP from domain.  one of these two
    102  * device nodes will be used:
    103  *   ETM_XPORT_DEV_FN_SP - the Ontario glvc
    104  *   ETM_XPORT_DEV_VLDC  - the more recent LDOMS 1.0 (a.k.a. Ontario+) vldc
    105  * When the latter is in use, use_vldc is set to 1.
    106  *
    107  * filenames of device nodes to reach domains from SP
    108  * are NA because SP runs ALOM vs Solaris or Linux
    109  * and ETM is for Unix based OSes
    110  */
    111 #define	ETM_XPORT_DEV_FN_SP	"/dev/spfma"
    112 
    113 #define	ETM_XPORT_DEV_VLDC	\
    114 	"/devices/virtual-devices@100/channel-devices@200" \
    115 	"/virtual-channel-client@2:spfma"
    116 
    117 /*
    118  * -------------------------- global variables -------------------------------
    119  */
    120 
    121 static int use_vldc = 0;
    122 
    123 static struct stats {
    124 
    125 	/* address handle failures */
    126 
    127 	fmd_stat_t xport_addr_magicnum_bad;
    128 	fmd_stat_t xport_addr_fn_bad;
    129 
    130 	/* connection handle failures */
    131 
    132 	fmd_stat_t xport_conn_magicnum_bad;
    133 	fmd_stat_t xport_conn_fd_bad;
    134 
    135 	/* internal read/peek failures */
    136 
    137 	fmd_stat_t xport_buffread_badargs;
    138 	fmd_stat_t xport_rawpeek_badargs;
    139 
    140 	/* xport API failures */
    141 
    142 	fmd_stat_t xport_accept_badargs;
    143 	fmd_stat_t xport_get_addr_conn_badargs;
    144 	fmd_stat_t xport_free_addr_badargs;
    145 	fmd_stat_t xport_free_addrv_badargs;
    146 	fmd_stat_t xport_get_any_lcc_badargs;
    147 
    148 	/* system and library failures */
    149 
    150 	fmd_stat_t xport_os_open_fail;
    151 	fmd_stat_t xport_os_close_fail;
    152 	fmd_stat_t xport_os_read_fail;
    153 	fmd_stat_t xport_os_write_fail;
    154 	fmd_stat_t xport_os_peek_fail;
    155 	fmd_stat_t xport_os_ioctl_fail;
    156 
    157 } etm_xport_stats = {
    158 
    159 	/* address handle failures */
    160 
    161 	{ "xport_addr_magicnum_bad", FMD_TYPE_UINT64,
    162 		"invalid address handle magic number" },
    163 	{ "xport_addr_fn_bad", FMD_TYPE_UINT64,
    164 		"invalid address handle file name" },
    165 
    166 	/* connection handle failures */
    167 
    168 	{ "xport_conn_magicnum_bad", FMD_TYPE_UINT64,
    169 		"invalid connection handle magic number" },
    170 	{ "xport_conn_fd_bad", FMD_TYPE_UINT64,
    171 		"invalid connection handle file descriptor" },
    172 
    173 	/* internal read/peek failures */
    174 
    175 	{ "xport_buffread_badargs", FMD_TYPE_UINT64,
    176 		"bad arguments in etm_xport_buffered_read" },
    177 	{ "xport_rawpeek_badargs", FMD_TYPE_UINT64,
    178 		"bad arguments in etm_xport_raw_peek" },
    179 
    180 	/* xport API failures */
    181 
    182 	{ "xport_accept_badargs", FMD_TYPE_UINT64,
    183 		"bad arguments in etm_xport_accept" },
    184 	{ "xport_get_addr_conn_badargs", FMD_TYPE_UINT64,
    185 		"bad arguments in etm_xport_get_addr_conn" },
    186 	{ "xport_free_addr_badargs", FMD_TYPE_UINT64,
    187 		"bad arguments in etm_xport_free_addr" },
    188 	{ "xport_free_addrv_badargs", FMD_TYPE_UINT64,
    189 		"bad arguments in etm_xport_free_addrv" },
    190 	{ "xport_get_any_lcc_badargs", FMD_TYPE_UINT64,
    191 		"bad arguments in etm_xport_get_any_lcc" },
    192 
    193 	/* system and library failures */
    194 
    195 	{ "xport_os_open_fail", FMD_TYPE_UINT64,
    196 		"open system call failures" },
    197 	{ "xport_os_close_fail", FMD_TYPE_UINT64,
    198 		"close system call failures" },
    199 	{ "xport_os_read_fail", FMD_TYPE_UINT64,
    200 		"read system call failures" },
    201 	{ "xport_os_write_fail", FMD_TYPE_UINT64,
    202 		"write system call failures" },
    203 	{ "xport_os_peek_fail", FMD_TYPE_UINT64,
    204 		"peek (ioctl) failures" },
    205 	{ "xport_os_ioctl_fail", FMD_TYPE_UINT64,
    206 		"ioctl system call failures" }
    207 };
    208 
    209 /* intermediate read buffer to [partially] emulate byte stream semantics */
    210 
    211 static uint8_t	*etm_xport_irb_area = NULL;	/* buffered read area */
    212 static uint8_t	*etm_xport_irb_head = NULL;	/* read head (dequeue) */
    213 static uint8_t	*etm_xport_irb_tail = NULL;	/* read tail (enqueue) */
    214 static size_t	etm_xport_irb_mtu_sz = 0;	/* MTU size (in bytes) */
    215 
    216 /*
    217  * -------------------------- private variables ------------------------------
    218  */
    219 
    220 static _etm_xport_conn_t *
    221 etm_xport_vldc_conn = NULL;	/* single connection handle for VLDC */
    222 
    223 static pthread_mutex_t
    224 etm_xport_vldc_lock = PTHREAD_MUTEX_INITIALIZER;
    225 				/* lock for open()/close() VLDC */
    226 
    227 static int
    228 etm_xport_debug_lvl = 0;	/* debug level: 0 off, 1 on, 2 more, ... */
    229 
    230 static char *
    231 etm_xport_addrs = "";		/* spec str for transport addrs to use */
    232 
    233 static int
    234 etm_xport_should_fake_dd = 0;	/* bool for whether to fake device driver */
    235 
    236 /*
    237  * -------------------------- private functions ------------------------------
    238  */
    239 
    240 /*
    241  * etm_fake_ioctl - fake/simulate transport driver's ioctl() behavior
    242  *			[for unit testing with device driver absent or
    243  *			for alternative directory entry based transports],
    244  *			return 0 for success
    245  *			or -1 and set errno
    246  * caveats:
    247  *		simulation may be incomplete, especially wrt peek()
    248  *
    249  * Design_Note:	To avoid interfering with FMD's signal mask (SIGALRM)
    250  *		do not use [Solaris] sleep(3C) and instead use
    251  *		pthread_cond_wait() or nanosleep(), both of which
    252  *		are POSIX spec-ed to leave signal masks alone.
    253  *		This is needed for Solaris and Linux (domain and SP).
    254  */
    255 
    256 static int
    257 etm_fake_ioctl(int fd, int op, void *buf)
    258 {
    259 	int			rv;		/* ret val */
    260 	etm_xport_opt_op_t	*op_ctl_ptr;	/* ptr for option ops */
    261 	etm_xport_msg_peek_t	*peek_ctl_ptr;	/* ptr for peeking */
    262 	struct stat		stat_buf;	/* file stat struct */
    263 	ssize_t			n;		/* gen use */
    264 	struct timespec		tms;		/* for nanosleep() */
    265 
    266 	tms.tv_sec = 0;
    267 	tms.tv_nsec = 0;
    268 
    269 	rv = 0; /* default is success */
    270 
    271 	if (op == ETM_XPORT_IOCTL_DATA_PEEK) {
    272 		peek_ctl_ptr = buf;
    273 		/* sleep until some data avail, potentially forever */
    274 		for (;;) {
    275 			if (fstat(fd, &stat_buf) < 0) {
    276 				rv = -1;
    277 				goto func_ret;
    278 			}
    279 			if (stat_buf.st_size > 0) {
    280 				n = MIN(peek_ctl_ptr->pk_buflen,
    281 				    stat_buf.st_size);
    282 				peek_ctl_ptr->pk_buflen = n;
    283 				/* return bogus data assuming content unused */
    284 				(void) memset(peek_ctl_ptr->pk_buf, 0xA5, n);
    285 				goto func_ret;
    286 			}
    287 			tms.tv_sec = ETM_SLEEP_QUIK;
    288 			tms.tv_nsec = 0;
    289 			if ((n = nanosleep(&tms, NULL)) < 0) {
    290 				rv = -1;
    291 				goto func_ret;
    292 			}
    293 		} /* forever awaiting data */
    294 	} else if (op == ETM_XPORT_IOCTL_OPT_OP) {
    295 		op_ctl_ptr = buf;
    296 		/* default near MTU_SZ gets and agree with everything else */
    297 		if ((op_ctl_ptr->oo_op  == ETM_XPORT_OPT_GET) &&
    298 		    (op_ctl_ptr->oo_opt == ETM_XPORT_OPT_MTU_SZ)) {
    299 			op_ctl_ptr->oo_val = 7 * ETM_XPORT_MTU_SZ_DEF / 8;
    300 		}
    301 		goto func_ret;
    302 	} /* whether ioctl op is handled */
    303 
    304 	rv = -1;
    305 	errno = EINVAL;
    306 
    307 func_ret:
    308 
    309 	return (rv);
    310 
    311 } /* etm_fake_ioctl() */
    312 
    313 /*
    314  * etm_xport_get_fn - return a cached read-only copy
    315  *			of the device node name to use
    316  *			for the given I/O operation
    317  */
    318 
    319 static char *
    320 etm_xport_get_fn(fmd_hdl_t *hdl, int io_op)
    321 {
    322 	static char	fn_wr[PATH_MAX] = {0};		/* fn for write */
    323 	static char	fn_rd[PATH_MAX] = {0};		/* fn for read/peek */
    324 	char		*rv;				/* ret val */
    325 	char		*prop_str;			/* property string */
    326 	char		*cp;				/* char ptr */
    327 
    328 	rv = NULL;
    329 
    330 	/* use cached copies if avail */
    331 
    332 	if ((io_op == ETM_IO_OP_WR) && (fn_wr[0] != '\0')) {
    333 		return (fn_wr);
    334 	}
    335 	if (((io_op == ETM_IO_OP_RD) || (io_op == ETM_IO_OP_PK)) &&
    336 	    (fn_rd[0] != '\0')) {
    337 		return (fn_rd);
    338 	}
    339 
    340 	/* create cached copies if empty "" property string */
    341 
    342 	prop_str = fmd_prop_get_string(hdl, ETM_PROP_NM_XPORT_ADDRS);
    343 	if (etm_xport_debug_lvl >= 2) {
    344 		fmd_hdl_debug(hdl, "info: etm_xport_get_fn prop_str %s\n",
    345 		    prop_str);
    346 	}
    347 
    348 	if (strlen(prop_str) == 0) {
    349 		struct stat buf;
    350 		char *fname;
    351 
    352 		if (stat(ETM_XPORT_DEV_VLDC, &buf) == 0) {
    353 			use_vldc = 1;
    354 			fname = ETM_XPORT_DEV_VLDC;
    355 		} else {
    356 			use_vldc = 0;
    357 			fname = ETM_XPORT_DEV_FN_SP;
    358 		}
    359 
    360 		(void) strncpy(fn_wr, fname, PATH_MAX - 1);
    361 		(void) strncpy(fn_rd, fname, PATH_MAX - 1);
    362 		rv = fn_rd;
    363 		if (io_op == ETM_IO_OP_WR) {
    364 			rv = fn_wr;
    365 		}
    366 		goto func_ret;
    367 	} /* if no/empty property set */
    368 
    369 	/* create cached copies if "write[|read]" property string */
    370 
    371 	if (io_op == ETM_IO_OP_WR) {
    372 		(void) strncpy(fn_wr, prop_str, PATH_MAX - 1);
    373 		if ((cp = strchr(fn_wr, '|')) != NULL) {
    374 			*cp = '\0';
    375 		}
    376 		rv = fn_wr;
    377 	} else {
    378 		if ((cp = strchr(prop_str, '|')) != NULL) {
    379 			cp++;
    380 		} else {
    381 			cp = prop_str;
    382 		}
    383 		(void) strncpy(fn_rd, cp, PATH_MAX - 1);
    384 		rv = fn_rd;
    385 	} /* whether io op is write/read/peek */
    386 
    387 func_ret:
    388 
    389 	if (etm_xport_debug_lvl >= 2) {
    390 		fmd_hdl_debug(hdl, "info: etm_xport_get_fn fn_wr %s fn_rd %s\n",
    391 		    fn_wr, fn_rd);
    392 	}
    393 	fmd_prop_free_string(hdl, prop_str);
    394 	return (rv);
    395 
    396 } /* etm_xport_get_fn() */
    397 
    398 /*
    399  * etm_xport_valid_addr - validate the given transport address,
    400  *			return 0 if valid
    401  *			or -errno value if not
    402  */
    403 
    404 static int
    405 etm_xport_valid_addr(etm_xport_addr_t addr)
    406 {
    407 	_etm_xport_addr_t	*_addr;		/* transport address */
    408 	struct stat		stat_buf;	/* buffer for stat() results */
    409 
    410 	_addr = addr;
    411 
    412 	if (_addr == NULL) {
    413 		return (-EINVAL);
    414 	}
    415 
    416 	if (_addr->magic_num != ETM_XPORT_DD_MAGIC_ADDR) {
    417 		etm_xport_stats.xport_addr_magicnum_bad.fmds_value.ui64++;
    418 		return (-EFAULT);
    419 	}
    420 
    421 	if (stat(_addr->fn, &stat_buf) < 0) {
    422 		/* errno assumed set by above call */
    423 		etm_xport_stats.xport_addr_fn_bad.fmds_value.ui64++;
    424 		return (-errno);
    425 	}
    426 
    427 	return (0);
    428 
    429 } /* etm_xport_valid_addr() */
    430 
    431 /*
    432  * etm_xport_valid_conn - validate the given connection handle,
    433  *			return 0 if valid
    434  *			or -errno value if not
    435  */
    436 
    437 static int
    438 etm_xport_valid_conn(etm_xport_conn_t conn)
    439 {
    440 	_etm_xport_conn_t	*_conn;		/* connection handle */
    441 
    442 	_conn = conn;
    443 
    444 	if (_conn == NULL) {
    445 		return (-EINVAL);
    446 	}
    447 
    448 	if (_conn->magic_num != ETM_XPORT_DD_MAGIC_CONN) {
    449 		etm_xport_stats.xport_conn_magicnum_bad.fmds_value.ui64++;
    450 		return (-EFAULT);
    451 	}
    452 
    453 	if (_conn->fd <= -1) {
    454 		etm_xport_stats.xport_conn_fd_bad.fmds_value.ui64++;
    455 		return (-EBADF);
    456 	}
    457 
    458 	return (0);
    459 
    460 } /* etm_xport_valid_conn() */
    461 
    462 /*
    463  * etm_xport_free_addr -  free the given transport address
    464  */
    465 
    466 static void
    467 etm_xport_free_addr(fmd_hdl_t *hdl, etm_xport_addr_t addr)
    468 {
    469 	if (addr == NULL) {
    470 		etm_xport_stats.xport_free_addr_badargs.fmds_value.ui64++;
    471 		return;
    472 	}
    473 
    474 	fmd_hdl_free(hdl, addr, sizeof (_etm_xport_addr_t));
    475 
    476 } /* etm_xport_free_addr() */
    477 
    478 /*
    479  * etm_xport_dup_addr - duplicate the given transport address,
    480  *			which is to be freed separately,
    481  *			return the newly allocated transport address
    482  *			pending until possible to do so
    483  */
    484 
    485 static etm_xport_addr_t
    486 etm_xport_dup_addr(fmd_hdl_t *hdl, etm_xport_addr_t addr)
    487 {
    488 	etm_xport_addr_t new_addr;	/* new transport address */
    489 
    490 	new_addr = fmd_hdl_zalloc(hdl, sizeof (_etm_xport_addr_t), FMD_SLEEP);
    491 	(void) memcpy(new_addr, addr, sizeof (_etm_xport_addr_t));
    492 	return (new_addr);
    493 
    494 } /* etm_xport_dup_addr() */
    495 
    496 /*
    497  * etm_xport_raw_peek - try to peek N <= MTU bytes from the connection
    498  *			into the caller's given buffer,
    499  *			return how many bytes actually peeked
    500  *			or -errno value
    501  * caveats:
    502  *		peeked data is NOT guaranteed by all platform transports
    503  *		to remain enqueued if this process/thread crashes;
    504  *		this casts some doubt on the utility of this func
    505  *
    506  *		transport does NOT support peek sizes > MTU
    507  */
    508 
    509 static ssize_t
    510 etm_xport_raw_peek(fmd_hdl_t *hdl, _etm_xport_conn_t *_conn,
    511 			void *buf, size_t byte_cnt)
    512 {
    513 	ssize_t			rv;		/* ret val */
    514 	ssize_t			n;		/* gen use */
    515 	etm_xport_msg_peek_t	peek_ctl;	/* struct for peeking */
    516 
    517 	rv = 0;
    518 
    519 	/* sanity check args */
    520 
    521 	if ((hdl == NULL) || (_conn == NULL) || (buf == NULL)) {
    522 		etm_xport_stats.xport_rawpeek_badargs.fmds_value.ui64++;
    523 		return (-EINVAL);
    524 	}
    525 
    526 	if ((etm_xport_irb_mtu_sz > 0) && (byte_cnt > etm_xport_irb_mtu_sz)) {
    527 		etm_xport_stats.xport_rawpeek_badargs.fmds_value.ui64++;
    528 		return (-EINVAL);
    529 	}
    530 
    531 	/* try to peek requested amt of data */
    532 
    533 	peek_ctl.pk_buf = buf;
    534 	peek_ctl.pk_buflen = byte_cnt;
    535 	peek_ctl.pk_flags = 0;
    536 	peek_ctl.pk_rsvd = 0;
    537 
    538 	if (etm_xport_should_fake_dd) {
    539 		n = etm_fake_ioctl(_conn->fd, ETM_XPORT_IOCTL_DATA_PEEK,
    540 		    &peek_ctl);
    541 	} else {
    542 		n = ioctl(_conn->fd, ETM_XPORT_IOCTL_DATA_PEEK, &peek_ctl);
    543 	}
    544 	if (n < 0) {
    545 		/* errno assumed set by above call */
    546 		etm_xport_stats.xport_os_peek_fail.fmds_value.ui64++;
    547 		rv = (-errno);
    548 	} else {
    549 		rv = peek_ctl.pk_buflen;
    550 	}
    551 
    552 	if (etm_xport_debug_lvl >= 3) {
    553 		fmd_hdl_debug(hdl, "info: [fake] ioctl(_PEEK) ~= %d bytes\n",
    554 		    rv);
    555 	}
    556 	return (rv);
    557 
    558 } /* etm_xport_raw_peek() */
    559 
    560 /*
    561  * Design_Note:
    562  *
    563  * The transport device driver did not implement byte stream semantics
    564  * per the spec; its behavior is closer to that of a block device.
    565  * Consequently, ETM within its Transport API attempts to make the device
    566  * look like a byte stream by using an intermediate buffer in user space
    567  * and maintaining progress pointers within that buffer which is populated
    568  * in near-MTU sized reads. We think it's OK to leave the write side
    569  * implementation as it was originally written for byte stream semantics
    570  * because we were told subsequent write()s will pend until the earlier
    571  * content is read() at the remote end -- essentially each write() must be
    572  * paired with a single read() -- the device driver does not buffer any I/O.
    573  *
    574  * The early driver bugs of returning more data than requested (thus
    575  * causing buffer overrun corruptions/crashes) and requiring user buffers
    576  * to be stack based vs heap based, have both been corrected.
    577  */
    578 
    579 /*
    580  * etm_xport_buffered_read - try to read N <= MTU bytes from the connection
    581  *			or from an privately maintained intermediate buffer,
    582  *			into the caller's given buffer,
    583  *			return how many bytes actually read
    584  *			or -errno value
    585  *
    586  * caveats:
    587  *		simple buffer scheme consumes 2x MTU bytes of memory and
    588  *		may do unnecesssary memory copies for ease of coding
    589  */
    590 
    591 static ssize_t
    592 etm_xport_buffered_read(fmd_hdl_t *hdl, _etm_xport_conn_t *_conn,
    593 			void *buf, size_t byte_cnt)
    594 {
    595 	ssize_t		i, n;		/* gen use */
    596 
    597 	/* perform one-time initializations */
    598 
    599 	/*
    600 	 * Design_Note:
    601 	 *
    602 	 * These initializations are not done in etm_xport_init() because
    603 	 * the connection/device is not yet open and hence the MTU size
    604 	 * is not yet known. However, the corresponding cleanup is done
    605 	 * in etm_xport_fini(). The buffering for byte stream semantics
    606 	 * should be done on a per device vs per connection basis; the
    607 	 * MTU size is assumed to remain constant across all connections.
    608 	 */
    609 
    610 	if (etm_xport_irb_mtu_sz == 0) {
    611 		if ((n = etm_xport_get_opt(hdl, _conn,
    612 		    ETM_XPORT_OPT_MTU_SZ)) < 0) {
    613 			etm_xport_irb_mtu_sz = ETM_XPORT_MTU_SZ_DEF;
    614 		} else {
    615 			etm_xport_irb_mtu_sz = n;
    616 		}
    617 	}
    618 	if (etm_xport_irb_area == NULL) {
    619 		etm_xport_irb_area = fmd_hdl_zalloc(hdl,
    620 		    2 * etm_xport_irb_mtu_sz, FMD_SLEEP);
    621 		etm_xport_irb_head = etm_xport_irb_area;
    622 		etm_xport_irb_tail = etm_xport_irb_head;
    623 	}
    624 
    625 	/* sanity check the byte count after have MTU */
    626 
    627 	if (byte_cnt > etm_xport_irb_mtu_sz) {
    628 		etm_xport_stats.xport_buffread_badargs.fmds_value.ui64++;
    629 		return (-EINVAL);
    630 	}
    631 
    632 	/* if intermediate buffer can satisfy request do so w/out xport read */
    633 
    634 	if (byte_cnt <= (etm_xport_irb_tail - etm_xport_irb_head)) {
    635 		(void) memcpy(buf, etm_xport_irb_head, byte_cnt);
    636 		etm_xport_irb_head += byte_cnt;
    637 		if (etm_xport_debug_lvl >= 2) {
    638 			fmd_hdl_debug(hdl, "info: quik buffered read == %d\n",
    639 			    byte_cnt);
    640 		}
    641 		return (byte_cnt);
    642 	}
    643 
    644 	/* slide buffer contents to front to make room for [MTU] more bytes */
    645 
    646 	n = etm_xport_irb_tail - etm_xport_irb_head;
    647 	(void) memmove(etm_xport_irb_area, etm_xport_irb_head, n);
    648 	etm_xport_irb_head = etm_xport_irb_area;
    649 	etm_xport_irb_tail = etm_xport_irb_head + n;
    650 
    651 	/*
    652 	 * peek to see how much data is avail and read all of it;
    653 	 * there is no race condition between peeking and reading
    654 	 * due to unbuffered design of the device driver
    655 	 */
    656 	if (use_vldc) {
    657 		pollfd_t pollfd;
    658 
    659 		pollfd.events = POLLIN;
    660 		pollfd.revents = 0;
    661 		pollfd.fd = _conn->fd;
    662 
    663 		if ((n = poll(&pollfd, 1, -1)) < 1) {
    664 			if (n == 0)
    665 				return (-EIO);
    666 			else
    667 				return (-errno);
    668 		}
    669 
    670 		/*
    671 		 * set i to the maximum size --- read(..., i) below will
    672 		 * pull in n bytes (n <= i) anyway
    673 		 */
    674 		i = etm_xport_irb_mtu_sz;
    675 	} else {
    676 		if ((i = etm_xport_raw_peek(hdl, _conn, etm_xport_irb_tail,
    677 		    etm_xport_irb_mtu_sz)) < 0) {
    678 			return (i);
    679 		}
    680 	}
    681 	if ((n = read(_conn->fd, etm_xport_irb_tail, i)) < 0) {
    682 		/* errno assumed set by above call */
    683 		etm_xport_stats.xport_os_read_fail.fmds_value.ui64++;
    684 		return (-errno);
    685 	}
    686 	etm_xport_irb_tail += n;
    687 
    688 	/* satisfy request as best we can with what we now have */
    689 
    690 	n = MIN(byte_cnt, (etm_xport_irb_tail - etm_xport_irb_head));
    691 	(void) memcpy(buf, etm_xport_irb_head, n);
    692 	etm_xport_irb_head += n;
    693 	if (etm_xport_debug_lvl >= 2) {
    694 		fmd_hdl_debug(hdl, "info: slow buffered read == %d\n", n);
    695 	}
    696 	return (n);
    697 
    698 } /* etm_xport_buffered_read() */
    699 
    700 /*
    701  * ------------------ connection establishment functions ---------------------
    702  */
    703 
    704 /*
    705  * etm_xport_init - initialize/setup any transport infrastructure
    706  *			before any connections are opened,
    707  *			return 0 or -errno value if initialization failed
    708  */
    709 
    710 int
    711 etm_xport_init(fmd_hdl_t *hdl)
    712 {
    713 	_etm_xport_addr_t	**_addrv;	/* address vector */
    714 	int			i;		/* vector index */
    715 	ssize_t			n;		/* gen use */
    716 	int			rv;		/* ret val */
    717 	struct stat		stat_buf;	/* file stat struct */
    718 	char			*fn;		/* filename of dev node */
    719 
    720 	rv = 0;	/* assume good */
    721 
    722 	_addrv = NULL;
    723 
    724 	if (hdl == NULL) {
    725 		rv = (-EINVAL);
    726 		goto func_ret;
    727 	}
    728 
    729 	fmd_hdl_debug(hdl, "info: xport initializing\n");
    730 
    731 	/* setup statistics and properties from FMD */
    732 
    733 	(void) fmd_stat_create(hdl, FMD_STAT_NOALLOC,
    734 	    sizeof (etm_xport_stats) / sizeof (fmd_stat_t),
    735 	    (fmd_stat_t *)&etm_xport_stats);
    736 
    737 	etm_xport_debug_lvl = fmd_prop_get_int32(hdl, ETM_PROP_NM_DEBUG_LVL);
    738 	etm_xport_addrs = fmd_prop_get_string(hdl, ETM_PROP_NM_XPORT_ADDRS);
    739 	fmd_hdl_debug(hdl, "info: etm_xport_debug_lvl %d\n",
    740 	    etm_xport_debug_lvl);
    741 	fmd_hdl_debug(hdl, "info: etm_xport_addrs %s\n", etm_xport_addrs);
    742 
    743 	/* decide whether to fake [some of] the device driver behavior */
    744 
    745 	etm_xport_should_fake_dd = 0;	/* default to false */
    746 
    747 	fn = etm_xport_get_fn(hdl, ETM_IO_OP_RD);
    748 	if (stat(fn, &stat_buf) < 0) {
    749 		/* errno assumed set by above call */
    750 		fmd_hdl_error(hdl, "error: bad device node %s errno %d\n",
    751 		    fn, errno);
    752 		rv = (-errno);
    753 		goto func_ret;
    754 	}
    755 	if (!S_ISCHR(stat_buf.st_mode) && use_vldc == 0) {
    756 		etm_xport_should_fake_dd = 1;	/* not a char driver */
    757 	}
    758 	fmd_hdl_debug(hdl, "info: etm_xport_should_fake_dd %d\n",
    759 	    etm_xport_should_fake_dd);
    760 
    761 	/* validate each default dst transport address */
    762 
    763 	if ((_addrv = (void *)etm_xport_get_ev_addrv(hdl, NULL)) == NULL) {
    764 		/* errno assumed set by above call */
    765 		rv = (-errno);
    766 		goto func_ret;
    767 	}
    768 
    769 	for (i = 0; _addrv[i] != NULL; i++) {
    770 		if ((n = etm_xport_valid_addr(_addrv[i])) < 0) {
    771 			fmd_hdl_error(hdl, "error: bad xport addr %p\n",
    772 			    _addrv[i]);
    773 			rv = n;
    774 			goto func_ret;
    775 		}
    776 	} /* foreach dst addr */
    777 
    778 	if (use_vldc) {
    779 		etm_xport_vldc_conn = etm_xport_open(hdl, _addrv[0]);
    780 		if (etm_xport_vldc_conn == NULL) {
    781 			fmd_hdl_debug(hdl, "info: etm_xport_open() failed\n");
    782 		}
    783 	}
    784 
    785 func_ret:
    786 
    787 	if (_addrv != NULL) {
    788 		etm_xport_free_addrv(hdl, (void *)_addrv);
    789 	}
    790 	if (rv >= 0) {
    791 		fmd_hdl_debug(hdl, "info: xport initialized ok\n");
    792 	}
    793 	return (rv);
    794 
    795 } /* etm_xport_init() */
    796 
    797 /*
    798  * etm_xport_open - open a connection with the given endpoint,
    799  *			return the connection handle,
    800  *			or NULL and set errno if open failed
    801  *
    802  * Design_Note: The current transport device driver's open()
    803  *		call will succeed even if the SP is down;
    804  *		hence there's currently no need for a retry
    805  *		mechanism.
    806  */
    807 
    808 etm_xport_conn_t
    809 etm_xport_open(fmd_hdl_t *hdl, etm_xport_addr_t addr)
    810 {
    811 	_etm_xport_addr_t	*_addr;		/* address handle */
    812 	_etm_xport_conn_t	*_conn;		/* connection handle */
    813 	ssize_t			n;		/* gen use */
    814 
    815 	if ((n = etm_xport_valid_addr(addr)) < 0) {
    816 		errno = (-n);
    817 		return (NULL);
    818 	}
    819 
    820 	_addr = etm_xport_dup_addr(hdl, addr);
    821 
    822 	/* allocate a connection handle and start populating it */
    823 
    824 	_conn = fmd_hdl_zalloc(hdl, sizeof (_etm_xport_conn_t), FMD_SLEEP);
    825 
    826 	(void) pthread_mutex_lock(&etm_xport_vldc_lock);
    827 
    828 	if (use_vldc == 0 || etm_xport_vldc_conn == NULL) {
    829 		if ((_conn->fd = open(_addr->fn,
    830 		    ETM_XPORT_OPEN_FLAGS, 0)) == -1) {
    831 			/* errno assumed set by above call */
    832 			etm_xport_free_addr(hdl, _addr);
    833 			fmd_hdl_free(hdl, _conn, sizeof (_etm_xport_conn_t));
    834 			etm_xport_stats.xport_os_open_fail.fmds_value.ui64++;
    835 			(void) pthread_mutex_unlock(&etm_xport_vldc_lock);
    836 			return (NULL);
    837 		}
    838 	}
    839 
    840 	if (use_vldc && etm_xport_vldc_conn == NULL) {
    841 		vldc_opt_op_t op;
    842 
    843 		/* Set the channel to reliable mode */
    844 		op.op_sel = VLDC_OP_SET;
    845 		op.opt_sel = VLDC_OPT_MODE;
    846 		op.opt_val = LDC_MODE_RELIABLE;
    847 
    848 		if (ioctl(_conn->fd, VLDC_IOCTL_OPT_OP, &op) != 0) {
    849 			/* errno assumed set by above call */
    850 			(void) close(_conn->fd);
    851 			etm_xport_free_addr(hdl, _addr);
    852 			fmd_hdl_free(hdl, _conn, sizeof (_etm_xport_conn_t));
    853 			etm_xport_stats.xport_os_ioctl_fail.fmds_value.ui64++;
    854 			(void) pthread_mutex_unlock(&etm_xport_vldc_lock);
    855 			return (NULL);
    856 		}
    857 
    858 		etm_xport_vldc_conn = _conn;
    859 	} else if (use_vldc && etm_xport_vldc_conn != NULL) {
    860 		_conn->fd = dup(etm_xport_vldc_conn->fd);
    861 	}
    862 
    863 	(void) pthread_mutex_unlock(&etm_xport_vldc_lock);
    864 
    865 	/* return the fully formed connection handle */
    866 
    867 	_conn->magic_num = ETM_XPORT_DD_MAGIC_CONN;
    868 	_conn->addr = _addr;
    869 
    870 	return (_conn);
    871 
    872 } /* etm_xport_open() */
    873 
    874 /*
    875  * etm_xport_accept - accept a request to open a connection,
    876  *			pending until a remote endpoint opens a
    877  *			a new connection to us [and sends an ETM msg],
    878  *			per non-NULL addrp optionally indicate the
    879  *			remote address if known/avail (NULL if not),
    880  *			return the connection handle,
    881  *			or NULL and set errno on failure
    882  *
    883  * caveats:
    884  *		any returned transport address is valid only for
    885  *		as long as the associated connection remains open;
    886  *		callers should not try to free the transport address
    887  *
    888  *		if new connections are rapid relative to how
    889  *		frequently this function is called, fairness will
    890  *		be provided among which connections are accepted
    891  *
    892  *		this function may maintain state to recognize [new]
    893  *		connections and/or to provide fairness
    894  */
    895 
    896 etm_xport_conn_t
    897 etm_xport_accept(fmd_hdl_t *hdl, etm_xport_addr_t *addrp)
    898 {
    899 	_etm_xport_addr_t	*_addr;	/* address handle */
    900 	_etm_xport_addr_t	**_addrv; /* vector of addresses */
    901 	_etm_xport_conn_t	*_conn;	/* connection handle */
    902 	_etm_xport_conn_t	*rv;	/* ret val */
    903 	uint8_t			buf[4];	/* buffer for peeking */
    904 	int			n;	/* byte cnt */
    905 	struct timespec		tms;	/* for nanosleep() */
    906 
    907 	rv = NULL;	/* default is failure */
    908 
    909 	_conn = NULL;
    910 	_addrv = NULL;
    911 
    912 	tms.tv_sec = ETM_SLEEP_QUIK;
    913 	tms.tv_nsec = 0;
    914 
    915 	/*
    916 	 * get the default dst transport address and open a connection to it;
    917 	 * there is only 1 default addr
    918 	 */
    919 
    920 	if ((_addrv = (void*)etm_xport_get_ev_addrv(hdl, NULL)) == NULL) {
    921 		/* errno assumed set by above call */
    922 		goto func_ret;
    923 	}
    924 
    925 	if (_addrv[0] == NULL) {
    926 		errno = ENXIO;	/* missing addr */
    927 		etm_xport_stats.xport_accept_badargs.fmds_value.ui64++;
    928 		goto func_ret;
    929 	}
    930 
    931 	if (_addrv[1] != NULL) {
    932 		errno = E2BIG;	/* too many addrs */
    933 		etm_xport_stats.xport_accept_badargs.fmds_value.ui64++;
    934 		goto func_ret;
    935 	}
    936 
    937 	_addr = _addrv[0];
    938 	_addr->fn = etm_xport_get_fn(hdl, ETM_IO_OP_RD);
    939 
    940 	if ((_conn = etm_xport_open(hdl, _addr)) == NULL) {
    941 		/* errno assumed set by above call */
    942 		goto func_ret;
    943 	}
    944 
    945 	if (etm_xport_should_fake_dd) {
    946 		(void) nanosleep(&tms, NULL);	/* delay [for resp capture] */
    947 		(void) ftruncate(_conn->fd, 0); /* act like socket/queue/pipe */
    948 	}
    949 
    950 	/*
    951 	 * peek from the connection to simulate an accept() system call
    952 	 * behavior; this will pend until some ETM message is written
    953 	 * from the other end
    954 	 */
    955 
    956 	if (use_vldc) {
    957 		pollfd_t pollfd;
    958 
    959 		pollfd.events = POLLIN;
    960 		pollfd.revents = 0;
    961 		pollfd.fd = _conn->fd;
    962 
    963 		if ((n = poll(&pollfd, 1, -1)) < 1) {
    964 			if (n == 0) {
    965 				errno = EIO;
    966 			}
    967 			goto func_ret;
    968 		}
    969 	} else {
    970 		if ((n = etm_xport_raw_peek(hdl, _conn, buf, 1)) < 0) {
    971 			errno = (-n);
    972 			goto func_ret;
    973 		}
    974 	}
    975 
    976 	rv = _conn;	/* success, return the open connection */
    977 
    978 func_ret:
    979 
    980 	/* cleanup the connection if failed */
    981 
    982 	if (rv == NULL) {
    983 		if (_conn != NULL) {
    984 			(void) etm_xport_close(hdl, _conn);
    985 		}
    986 	} else {
    987 		if (addrp != NULL) {
    988 			*addrp = _conn->addr;
    989 		}
    990 	}
    991 
    992 	/* free _addrv and all its transport addresses */
    993 
    994 	if (_addrv != NULL) {
    995 		etm_xport_free_addrv(hdl, (void *)_addrv);
    996 	}
    997 
    998 	if (etm_xport_debug_lvl >= 2) {
    999 		fmd_hdl_debug(hdl, "info: accept conn %p w/ *addrp %p\n",
   1000 		    rv, (addrp != NULL ? *addrp : NULL));
   1001 	}
   1002 
   1003 	return (rv);
   1004 
   1005 } /* etm_xport_accept() */
   1006 
   1007 /*
   1008  * etm_xport_close - close a connection from either endpoint,
   1009  *			return the original connection handle,
   1010  *			or NULL and set errno if close failed
   1011  */
   1012 
   1013 etm_xport_conn_t
   1014 etm_xport_close(fmd_hdl_t *hdl, etm_xport_conn_t conn)
   1015 {
   1016 	etm_xport_conn_t	rv;	/* ret val */
   1017 	_etm_xport_conn_t	*_conn;	/* connection handle */
   1018 	int			nev;	/* -errno val */
   1019 
   1020 	_conn = conn;
   1021 
   1022 	rv = _conn;	/* assume success */
   1023 
   1024 	if ((nev = etm_xport_valid_conn(_conn)) < 0) {
   1025 		_conn = NULL;
   1026 		rv = NULL;
   1027 		goto func_ret;
   1028 	}
   1029 
   1030 	/* close the device node */
   1031 
   1032 	(void) pthread_mutex_lock(&etm_xport_vldc_lock);
   1033 
   1034 	if (close(_conn->fd) < 0) {
   1035 		/* errno assumed set by above call */
   1036 		etm_xport_stats.xport_os_close_fail.fmds_value.ui64++;
   1037 		nev = (-errno);
   1038 		rv = NULL;
   1039 	}
   1040 
   1041 	if (use_vldc && (_conn == etm_xport_vldc_conn)) {
   1042 		etm_xport_vldc_conn = NULL;
   1043 	}
   1044 
   1045 	(void) pthread_mutex_unlock(&etm_xport_vldc_lock);
   1046 
   1047 func_ret:
   1048 
   1049 	/* cleanup the connection */
   1050 
   1051 	if (_conn != NULL) {
   1052 		etm_xport_free_addr(hdl, _conn->addr);
   1053 		_conn->addr = NULL;
   1054 		_conn->magic_num = 0;
   1055 		_conn->fd = -1;
   1056 		fmd_hdl_free(hdl, _conn, sizeof (_etm_xport_conn_t));
   1057 	}
   1058 
   1059 	if (rv == NULL) {
   1060 		errno = (-nev);
   1061 	}
   1062 	return (rv);
   1063 
   1064 } /* etm_xport_close() */
   1065 
   1066 /*
   1067  * etm_xport_get_ev_addrv - indicate which transport addresses
   1068  *				are implied as destinations by the
   1069  *				given FMA event, if given no FMA event
   1070  *				(NULL) indicate default or policy
   1071  *				driven dst transport addresses,
   1072  *				return an allocated NULL terminated
   1073  *				vector of allocated transport addresses,
   1074  *				or NULL and set errno if none
   1075  * caveats:
   1076  *		callers should never try to individually free an addr
   1077  *		within the returned vector
   1078  */
   1079 
   1080 etm_xport_addr_t *
   1081 etm_xport_get_ev_addrv(fmd_hdl_t *hdl, nvlist_t *evp)
   1082 {
   1083 	_etm_xport_addr_t	*_addr;		/* address handle */
   1084 	_etm_xport_addr_t	**_addrv;	/* vector of addresses */
   1085 
   1086 	if (evp == NULL) {
   1087 
   1088 		/*
   1089 		 * allocate address handles for default/policy destinations
   1090 		 *
   1091 		 * in reality we have just 1 dst transport addr
   1092 		 */
   1093 
   1094 		_addr = fmd_hdl_zalloc(hdl, sizeof (_etm_xport_addr_t),
   1095 		    FMD_SLEEP);
   1096 	} else {
   1097 
   1098 		/*
   1099 		 * allocate address handles per FMA event content
   1100 		 *
   1101 		 * in reality we have just 1 dst transport addr
   1102 		 */
   1103 
   1104 		_addr = fmd_hdl_zalloc(hdl, sizeof (_etm_xport_addr_t),
   1105 		    FMD_SLEEP);
   1106 	} /* whether caller passed in a FMA event */
   1107 
   1108 	/* allocate vector with 1 non-NULL transport addr */
   1109 
   1110 	_addrv = fmd_hdl_zalloc(hdl, 2 * sizeof (_etm_xport_addr_t *),
   1111 	    FMD_SLEEP);
   1112 
   1113 	_addr->fn = etm_xport_get_fn(hdl, ETM_IO_OP_WR);
   1114 	_addr->magic_num = ETM_XPORT_DD_MAGIC_ADDR;
   1115 	_addrv[0] = _addr;
   1116 	_addrv[1] = NULL;
   1117 
   1118 	return ((void *) _addrv);
   1119 
   1120 } /* etm_xport_get_ev_addrv() */
   1121 
   1122 /*
   1123  * etm_xport_free_addrv - free the given vector of transport addresses,
   1124  *				including each transport address
   1125  */
   1126 
   1127 void
   1128 etm_xport_free_addrv(fmd_hdl_t *hdl, etm_xport_addr_t *addrv)
   1129 {
   1130 	_etm_xport_addr_t	**_addrv;	/* vector of addrs */
   1131 	int			i;		/* vector index */
   1132 
   1133 	if (addrv == NULL) {
   1134 		etm_xport_stats.xport_free_addrv_badargs.fmds_value.ui64++;
   1135 		return;
   1136 	}
   1137 
   1138 	_addrv = (void*)addrv;
   1139 
   1140 	for (i = 0; _addrv[i] != NULL; i++) {
   1141 		etm_xport_free_addr(hdl, _addrv[i]);
   1142 		_addrv[i] = NULL;
   1143 	}
   1144 	fmd_hdl_free(hdl, _addrv, (i + 1) * sizeof (_etm_xport_addr_t *));
   1145 
   1146 } /* etm_xport_free_addrv() */
   1147 
   1148 /*
   1149  * etm_xport_get_addr_conn - indicate which connections in a NULL
   1150  *				terminated vector of connection
   1151  *				handles are associated with the
   1152  *				given transport address,
   1153  *				return an allocated NULL terminated
   1154  *				vector of those connection handles,
   1155  *				or NULL and set errno if none
   1156  */
   1157 
   1158 etm_xport_conn_t *
   1159 etm_xport_get_addr_conn(fmd_hdl_t *hdl, etm_xport_conn_t *connv,
   1160 			    etm_xport_addr_t addr)
   1161 {
   1162 	_etm_xport_conn_t	**_connv; /* vector of connections */
   1163 	_etm_xport_conn_t	**_mcv;	/* matching connections vector */
   1164 	_etm_xport_addr_t	*_addr;	/* transport addr to match */
   1165 	int			n;	/* matching transport addr cnt */
   1166 	int			i;	/* vector index */
   1167 
   1168 	if ((connv == NULL) || (addr == NULL)) {
   1169 		errno = EINVAL;
   1170 		etm_xport_stats.xport_get_addr_conn_badargs.fmds_value.ui64++;
   1171 		return (NULL);
   1172 	}
   1173 
   1174 	_connv = (void*)connv;
   1175 	_addr = (void*)addr;
   1176 
   1177 	/* count, allocate space for, and copy, all matching addrs */
   1178 
   1179 	n = 0;
   1180 	for (i = 0; _connv[i] != NULL; i++) {
   1181 		if ((_connv[i]->addr == _addr) ||
   1182 		    ((_connv[i]->addr != NULL) &&
   1183 		    (_connv[i]->addr->fn == _addr->fn))) {
   1184 			n++;
   1185 		}
   1186 	} /* for counting how many addresses match */
   1187 
   1188 	_mcv = fmd_hdl_zalloc(hdl, (n + 1) * sizeof (_etm_xport_conn_t *),
   1189 	    FMD_SLEEP);
   1190 	n = 0;
   1191 	for (i = 0; _connv[i] != NULL; i++) {
   1192 		if ((_connv[i]->addr == _addr) ||
   1193 		    ((_connv[i]->addr != NULL) &&
   1194 		    (_connv[i]->addr->fn == _addr->fn))) {
   1195 			_mcv[n] = _connv[i];
   1196 			n++;
   1197 		}
   1198 	} /* for copying matching address pointers */
   1199 	_mcv[n] = NULL;
   1200 
   1201 	return ((void *) _mcv);
   1202 
   1203 } /* etm_xport_get_addr_conn() */
   1204 
   1205 /*
   1206  * etm_xport_get_any_lcc - indicate which endpoint has undergone
   1207  *			a life cycle change and what that change
   1208  *			was (ex: came up), pending until a change
   1209  *			has occured for some/any endpoint,
   1210  *			return the appropriate address handle,
   1211  *			or NULL and set errno if problem
   1212  *
   1213  * caveats:
   1214  *		this function maintains or accesses state/history
   1215  *		regarding life cycle changes of endpoints
   1216  *
   1217  *		if life cycle changes are rapid relative to how
   1218  *		frequently this function is called, fairness will
   1219  *		be provided among which endpoints are reported
   1220  */
   1221 
   1222 etm_xport_addr_t
   1223 etm_xport_get_any_lcc(fmd_hdl_t *hdl, etm_xport_lcc_t *lccp)
   1224 {
   1225 	if ((hdl == NULL) || (lccp == NULL)) {
   1226 		etm_xport_stats.xport_get_any_lcc_badargs.fmds_value.ui64++;
   1227 		errno = EINVAL;
   1228 		return (NULL);
   1229 	}
   1230 
   1231 	/*
   1232 	 * function not needed in FMA Phase 1 for sun4v/Ontario
   1233 	 */
   1234 
   1235 	errno = ENOTSUP;
   1236 	return (NULL);
   1237 
   1238 } /* etm_xport_get_any_lcc() */
   1239 
   1240 /*
   1241  * etm_xport_fini - finish/teardown any transport infrastructure
   1242  *			after all connections are closed,
   1243  *			return 0 or -errno value if teardown failed
   1244  */
   1245 
   1246 int
   1247 etm_xport_fini(fmd_hdl_t *hdl)
   1248 {
   1249 	fmd_hdl_debug(hdl, "info: xport finalizing\n");
   1250 
   1251 	if (use_vldc && (etm_xport_vldc_conn != NULL)) {
   1252 		(void) etm_xport_close(hdl, etm_xport_vldc_conn);
   1253 		etm_xport_vldc_conn = NULL;
   1254 	}
   1255 
   1256 	/* free any long standing properties from FMD */
   1257 
   1258 	fmd_prop_free_string(hdl, etm_xport_addrs);
   1259 
   1260 	/* cleanup the intermediate read buffer */
   1261 
   1262 	if (etm_xport_irb_tail != etm_xport_irb_head) {
   1263 		fmd_hdl_debug(hdl, "warning: xport %d bytes stale data\n",
   1264 		    (int)(etm_xport_irb_tail - etm_xport_irb_head));
   1265 	}
   1266 	fmd_hdl_free(hdl, etm_xport_irb_area, 2 * etm_xport_irb_mtu_sz);
   1267 	etm_xport_irb_area = NULL;
   1268 	etm_xport_irb_head = NULL;
   1269 	etm_xport_irb_tail = NULL;
   1270 	etm_xport_irb_mtu_sz = 0;
   1271 
   1272 	/* cleanup statistics from FMD */
   1273 
   1274 	(void) fmd_stat_destroy(hdl,
   1275 	    sizeof (etm_xport_stats) / sizeof (fmd_stat_t),
   1276 	    (fmd_stat_t *)&etm_xport_stats);
   1277 
   1278 	fmd_hdl_debug(hdl, "info: xport finalized ok\n");
   1279 	return (0);
   1280 
   1281 } /* etm_xport_fini() */
   1282 
   1283 /*
   1284  * ------------------------ input/output functions ---------------------------
   1285  */
   1286 
   1287 /*
   1288  * etm_xport_read - try to read N bytes from the connection
   1289  *			into the given buffer,
   1290  *			return how many bytes actually read
   1291  *			or -errno value
   1292  */
   1293 
   1294 ssize_t
   1295 etm_xport_read(fmd_hdl_t *hdl, etm_xport_conn_t conn, void *buf,
   1296 							size_t byte_cnt)
   1297 {
   1298 	return (etm_xport_buffered_read(hdl, conn, buf, byte_cnt));
   1299 
   1300 } /* etm_xport_read() */
   1301 
   1302 /*
   1303  * etm_xport_write - try to write N bytes to the connection
   1304  *			from the given buffer,
   1305  *			return how many bytes actually written
   1306  *			or -errno value
   1307  */
   1308 
   1309 ssize_t
   1310 etm_xport_write(fmd_hdl_t *hdl, etm_xport_conn_t conn, void *buf,
   1311 							size_t byte_cnt)
   1312 {
   1313 	_etm_xport_conn_t	*_conn;		/* connection handle */
   1314 	int			n;		/* byte cnt */
   1315 
   1316 	_conn = conn;
   1317 
   1318 	if (hdl == NULL) {		/* appease lint */
   1319 		return (-EINVAL);
   1320 	}
   1321 	if ((n = etm_xport_valid_conn(_conn)) < 0) {
   1322 		return (n);
   1323 	}
   1324 
   1325 	/* write to the connection device's open file descriptor */
   1326 
   1327 	if ((n = write(_conn->fd, buf, byte_cnt)) < 0) {
   1328 		/* errno assumed set by above call */
   1329 		etm_xport_stats.xport_os_write_fail.fmds_value.ui64++;
   1330 		n = (-errno);
   1331 	}
   1332 
   1333 	return (n);
   1334 
   1335 } /* etm_xport_write() */
   1336 
   1337 /*
   1338  * ------------------------ miscellaneous functions --------------------------
   1339  */
   1340 
   1341 /*
   1342  * etm_xport_get_opt - get a connection's transport option value,
   1343  *			return the current value
   1344  *			or -errno value (ex: -ENOTSUP)
   1345  */
   1346 
   1347 ssize_t
   1348 etm_xport_get_opt(fmd_hdl_t *hdl, etm_xport_conn_t conn, etm_xport_opt_t opt)
   1349 {
   1350 	ssize_t			rv;		/* ret val */
   1351 	_etm_xport_conn_t	*_conn;		/* connection handle */
   1352 	etm_xport_opt_op_t	op_ctl;		/* struct for option ops */
   1353 	ssize_t			n;		/* gen use */
   1354 
   1355 	rv = 0;
   1356 	_conn = conn;
   1357 
   1358 	if (hdl == NULL) {		/* appease lint */
   1359 		return (-EINVAL);
   1360 	}
   1361 	if ((n = etm_xport_valid_conn(_conn)) < 0) {
   1362 		return (n);
   1363 	}
   1364 
   1365 	op_ctl.oo_op = ETM_XPORT_OPT_GET;
   1366 	op_ctl.oo_opt = opt;
   1367 
   1368 	if (etm_xport_should_fake_dd) {
   1369 		n = etm_fake_ioctl(_conn->fd, ETM_XPORT_IOCTL_OPT_OP, &op_ctl);
   1370 	} else if (use_vldc) {
   1371 		if (opt == ETM_XPORT_OPT_MTU_SZ) {
   1372 			vldc_opt_op_t operation;
   1373 
   1374 			operation.op_sel = VLDC_OP_GET;
   1375 			operation.opt_sel = VLDC_OPT_MTU_SZ;
   1376 
   1377 			n = ioctl(_conn->fd, VLDC_IOCTL_OPT_OP, &operation);
   1378 
   1379 			op_ctl.oo_val = operation.opt_val;
   1380 		} else {
   1381 			return (-EINVAL);
   1382 		}
   1383 	} else {
   1384 		n = ioctl(_conn->fd, ETM_XPORT_IOCTL_OPT_OP, &op_ctl);
   1385 	}
   1386 	if (n < 0) {
   1387 		/* errno assumed set by above call */
   1388 		rv = (-errno);
   1389 		etm_xport_stats.xport_os_ioctl_fail.fmds_value.ui64++;
   1390 	} else {
   1391 		rv = (int)op_ctl.oo_val;
   1392 	}
   1393 
   1394 	return (rv);
   1395 
   1396 } /* etm_xport_get_opt() */
   1397