xref: /titanic_52/usr/src/cmd/fm/modules/sun4v/etm/etm_xport_api_dd.c (revision 2ca9f232d5e039a8f2de8723786dbf6248bf9e1e)
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