xref: /illumos-gate/usr/src/uts/sun4v/io/ds_common.c (revision 9fa01faf81019677b9c27c7bdf6a310284d4ccb1)
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 2009 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*
28  * Domain Services Module Common Code.
29  *
30  * This module is intended to be used by both Solaris and the VBSC
31  * module.
32  */
33 
34 #include <sys/modctl.h>
35 #include <sys/ksynch.h>
36 #include <sys/taskq.h>
37 #include <sys/disp.h>
38 #include <sys/cmn_err.h>
39 #include <sys/note.h>
40 #include <sys/mach_descrip.h>
41 #include <sys/mdesc.h>
42 #include <sys/ldc.h>
43 #include <sys/ds.h>
44 #include <sys/ds_impl.h>
45 
46 #ifndef MIN
47 #define	MIN(a, b)	((a) < (b) ? (a) : (b))
48 #endif
49 
50 #define	DS_DECODE_BUF_LEN		30
51 
52 /*
53  * All DS ports in the system
54  *
55  * The list of DS ports is read in from the MD when the DS module is
56  * initialized and is never modified. This eliminates the need for
57  * locking to access the port array itself. Access to the individual
58  * ports are synchronized at the port level.
59  */
60 ds_port_t	ds_ports[DS_MAX_PORTS];
61 ds_portset_t	ds_allports;	/* all DS ports in the system */
62 ds_portset_t	ds_nullport;	/* allows test against null portset */
63 
64 /* DS SP port id */
65 uint64_t ds_sp_port_id = DS_PORTID_INVALID;
66 
67 /*
68  * Table of registered services
69  *
70  * Locking: Accesses to the table of services are synchronized using
71  *   a mutex lock. The reader lock must be held when looking up service
72  *   information in the table. The writer lock must be held when any
73  *   service information is being modified.
74  */
75 ds_svcs_t	ds_svcs;
76 
77 /*
78  * Flag to prevent callbacks while in the middle of DS teardown.
79  */
80 boolean_t ds_enabled = B_FALSE;	/* enable/disable taskq processing */
81 
82 /*
83  * Retry count and delay for LDC reads and writes
84  */
85 #ifndef DS_DEFAULT_RETRIES
86 #define	DS_DEFAULT_RETRIES	10000	/* number of times to retry */
87 #endif
88 #ifndef DS_DEFAULT_DELAY
89 #define	DS_DEFAULT_DELAY	1000	/* usecs to wait between retries */
90 #endif
91 
92 static int ds_retries = DS_DEFAULT_RETRIES;
93 static clock_t ds_delay = DS_DEFAULT_DELAY;
94 
95 /*
96  * Supported versions of the DS message protocol
97  *
98  * The version array must be sorted in order from the highest
99  * supported version to the lowest. Support for a particular
100  * <major>.<minor> version implies all lower minor versions of
101  * that same major version are supported as well.
102  */
103 static ds_ver_t ds_vers[] = { { 1, 0 } };
104 
105 #define	DS_NUM_VER	(sizeof (ds_vers) / sizeof (ds_vers[0]))
106 
107 
108 /* incoming message handling functions */
109 typedef void (*ds_msg_handler_t)(ds_port_t *port, caddr_t buf, size_t len);
110 static void ds_handle_init_req(ds_port_t *port, caddr_t buf, size_t len);
111 static void ds_handle_init_ack(ds_port_t *port, caddr_t buf, size_t len);
112 static void ds_handle_init_nack(ds_port_t *port, caddr_t buf, size_t len);
113 static void ds_handle_reg_req(ds_port_t *port, caddr_t buf, size_t len);
114 static void ds_handle_reg_ack(ds_port_t *port, caddr_t buf, size_t len);
115 static void ds_handle_reg_nack(ds_port_t *port, caddr_t buf, size_t len);
116 static void ds_handle_unreg_req(ds_port_t *port, caddr_t buf, size_t len);
117 static void ds_handle_unreg_ack(ds_port_t *port, caddr_t buf, size_t len);
118 static void ds_handle_unreg_nack(ds_port_t *port, caddr_t buf, size_t len);
119 static void ds_handle_data(ds_port_t *port, caddr_t buf, size_t len);
120 static void ds_handle_nack(ds_port_t *port, caddr_t buf, size_t len);
121 
122 /*
123  * DS Message Handler Dispatch Table
124  *
125  * A table used to dispatch all incoming messages. This table
126  * contains handlers for all the fixed message types, as well as
127  * the the messages defined in the 1.0 version of the DS protocol.
128  * The handlers are indexed based on the DS header msg_type values
129  */
130 static const ds_msg_handler_t ds_msg_handlers[] = {
131 	ds_handle_init_req,		/* DS_INIT_REQ */
132 	ds_handle_init_ack,		/* DS_INIT_ACK */
133 	ds_handle_init_nack,		/* DS_INIT_NACK */
134 	ds_handle_reg_req,		/* DS_REG_REQ */
135 	ds_handle_reg_ack,		/* DS_REG_ACK */
136 	ds_handle_reg_nack,		/* DS_REG_NACK */
137 	ds_handle_unreg_req,		/* DS_UNREG */
138 	ds_handle_unreg_ack,		/* DS_UNREG_ACK */
139 	ds_handle_unreg_nack,		/* DS_UNREG_NACK */
140 	ds_handle_data,			/* DS_DATA */
141 	ds_handle_nack			/* DS_NACK */
142 };
143 
144 
145 
146 /* initialization functions */
147 static int ds_ldc_init(ds_port_t *port);
148 
149 /* event processing functions */
150 static uint_t ds_ldc_cb(uint64_t event, caddr_t arg);
151 static int ds_recv_msg(ds_port_t *port, caddr_t msgp, size_t *sizep);
152 static void ds_handle_up_event(ds_port_t *port);
153 static void ds_handle_down_reset_events(ds_port_t *port);
154 static void ds_handle_recv(void *arg);
155 static void ds_dispatch_event(void *arg);
156 
157 /* message sending functions */
158 static int ds_send_msg(ds_port_t *port, caddr_t msg, size_t msglen);
159 static int ds_send_reg_req(ds_svc_t *svc, ds_port_t *port);
160 static void ds_send_unreg_nack(ds_port_t *port, ds_svc_hdl_t bad_hdl);
161 static void ds_send_data_nack(ds_port_t *port, ds_svc_hdl_t bad_hdl);
162 
163 /* walker functions */
164 static int ds_svc_isfree(ds_svc_t *svc, void *arg);
165 static int ds_svc_unregister(ds_svc_t *svc, void *arg);
166 static int ds_svc_port_up(ds_svc_t *svc, void *arg);
167 
168 /* service utilities */
169 static void ds_reset_svc(ds_svc_t *svc, ds_port_t *port);
170 static int ds_svc_register_onport(ds_svc_t *svc, ds_port_t *port);
171 
172 /* port utilities */
173 static void ds_port_reset(ds_port_t *port);
174 static ldc_status_t ds_update_ldc_state(ds_port_t *port);
175 
176 /* misc utilities */
177 static void min_max_versions(int num_versions, ds_ver_t *sup_versionsp,
178     uint16_t *min_major, uint16_t *max_major);
179 
180 /* debug */
181 static char *decode_ldc_events(uint64_t event, char *buf);
182 
183 /* loopback */
184 static void ds_loopback_register(ds_svc_hdl_t hdl);
185 static void ds_loopback_unregister(ds_svc_hdl_t hdl);
186 static void ds_loopback_send(ds_svc_hdl_t hdl, void *buf, size_t buflen);
187 static int ds_loopback_set_svc(ds_svc_t *svc, ds_capability_t *cap,
188     ds_svc_hdl_t *lb_hdlp);
189 
190 /* client handling */
191 static int i_ds_hdl_lookup(char *service, uint_t is_client, ds_svc_hdl_t *hdlp,
192     uint_t maxhdls);
193 static ds_svc_t *ds_find_clnt_svc_by_hdl_port(ds_svc_hdl_t hdl,
194     ds_port_t *port);
195 static ds_svc_t *ds_find_svc_by_id_port(char *svc_id, int is_client,
196     ds_port_t *port);
197 static ds_svc_t *ds_svc_clone(ds_svc_t *svc);
198 static void ds_portset_del_active_clients(char *service, ds_portset_t *portsp);
199 static void ds_check_for_dup_services(ds_svc_t *svc);
200 static void ds_delete_svc_entry(ds_svc_t *svc);
201 
202 char *
203 ds_strdup(char *str)
204 {
205 	char *newstr;
206 
207 	newstr = DS_MALLOC(strlen(str) + 1);
208 	(void) strcpy(newstr, str);
209 	return (newstr);
210 }
211 
212 void
213 ds_common_init(void)
214 {
215 	/* Validate version table */
216 	ASSERT(ds_vers_isvalid(ds_vers, DS_NUM_VER) == DS_VERS_OK);
217 
218 	/* Initialize services table */
219 	ds_init_svcs_tbl(DS_MAXSVCS_INIT);
220 
221 	/* enable callback processing */
222 	ds_enabled = B_TRUE;
223 }
224 
225 /* BEGIN LDC SUPPORT FUNCTIONS */
226 
227 static char *
228 decode_ldc_events(uint64_t event, char *buf)
229 {
230 	buf[0] = 0;
231 	if (event & LDC_EVT_DOWN)	(void) strcat(buf, " DOWN");
232 	if (event & LDC_EVT_RESET)	(void) strcat(buf, " RESET");
233 	if (event & LDC_EVT_UP)		(void) strcat(buf, " UP");
234 	if (event & LDC_EVT_READ)	(void) strcat(buf, " READ");
235 	if (event & LDC_EVT_WRITE)	(void) strcat(buf, " WRITE");
236 	return (buf);
237 }
238 
239 static ldc_status_t
240 ds_update_ldc_state(ds_port_t *port)
241 {
242 	ldc_status_t	ldc_state;
243 	int		rv;
244 	char		ebuf[DS_EBUFSIZE];
245 
246 	ASSERT(MUTEX_HELD(&port->lock));
247 
248 	/*
249 	 * Read status and update ldc state info in port structure.
250 	 */
251 	if ((rv = ldc_status(port->ldc.hdl, &ldc_state)) != 0) {
252 		cmn_err(CE_WARN, "ds@%lx: %s: ldc_status error: %s" DS_EOL,
253 		    PORTID(port), __func__, ds_errno_to_str(rv, ebuf));
254 		ldc_state = port->ldc.state;
255 	} else {
256 		port->ldc.state = ldc_state;
257 	}
258 
259 	return (ldc_state);
260 }
261 
262 static void
263 ds_handle_down_reset_events(ds_port_t *port)
264 {
265 	DS_DBG_LDC(CE_NOTE, "ds@%lx: %s: entered" DS_EOL, PORTID(port),
266 	    __func__);
267 
268 	mutex_enter(&ds_svcs.lock);
269 	mutex_enter(&port->lock);
270 
271 	ds_sys_drain_events(port);
272 
273 	(void) ds_update_ldc_state(port);
274 
275 	/* reset the port state */
276 	ds_port_reset(port);
277 
278 	/* acknowledge the reset */
279 	(void) ldc_up(port->ldc.hdl);
280 
281 	mutex_exit(&port->lock);
282 	mutex_exit(&ds_svcs.lock);
283 
284 	ds_handle_up_event(port);
285 
286 	DS_DBG_LDC(CE_NOTE, "ds@%lx: %s: exit" DS_EOL, PORTID(port), __func__);
287 }
288 
289 static void
290 ds_handle_up_event(ds_port_t *port)
291 {
292 	ldc_status_t	ldc_state;
293 
294 	DS_DBG_LDC(CE_NOTE, "ds@%lx: %s: entered" DS_EOL, PORTID(port),
295 	    __func__);
296 
297 	mutex_enter(&port->lock);
298 
299 	ldc_state = ds_update_ldc_state(port);
300 
301 	mutex_exit(&port->lock);
302 
303 	if ((ldc_state == LDC_UP) && IS_DS_PORT(port)) {
304 		/*
305 		 * Initiate the handshake.
306 		 */
307 		ds_send_init_req(port);
308 	}
309 
310 	DS_DBG_LDC(CE_NOTE, "ds@%lx: %s: exit" DS_EOL, PORTID(port), __func__);
311 }
312 
313 static uint_t
314 ds_ldc_cb(uint64_t event, caddr_t arg)
315 {
316 	ds_port_t	*port = (ds_port_t *)arg;
317 	char		evstring[DS_DECODE_BUF_LEN];
318 
319 	DS_DBG_LDC(CE_NOTE, "ds@%lx: %s: %s event (%llx) received" DS_EOL,
320 	    PORTID(port), __func__, decode_ldc_events(event, evstring),
321 	    (u_longlong_t)event);
322 
323 	if (!ds_enabled) {
324 		DS_DBG_LDC(CE_NOTE, "ds@%lx: %s: callback handling is disabled"
325 		    DS_EOL, PORTID(port), __func__);
326 		return (LDC_SUCCESS);
327 	}
328 
329 	if (event & (LDC_EVT_DOWN | LDC_EVT_RESET)) {
330 		ds_handle_down_reset_events(port);
331 		goto done;
332 	}
333 
334 	if (event & LDC_EVT_UP) {
335 		ds_handle_up_event(port);
336 	}
337 
338 	if (event & LDC_EVT_READ) {
339 		if (port->ldc.state != LDC_UP) {
340 			cmn_err(CE_WARN, "ds@%lx: %s: LDC READ event while "
341 			    "port not up" DS_EOL, PORTID(port), __func__);
342 			goto done;
343 		}
344 
345 		if (ds_sys_dispatch_func(ds_handle_recv, port)) {
346 			cmn_err(CE_WARN, "ds@%lx: error initiating LDC READ "
347 			    " event", PORTID(port));
348 		}
349 	}
350 
351 	if (event & LDC_EVT_WRITE) {
352 		DS_DBG_LDC(CE_NOTE, "ds@%lx: %s: LDC WRITE event received, "
353 		    "not supported" DS_EOL, PORTID(port), __func__);
354 	}
355 
356 	if (event & ~(LDC_EVT_UP | LDC_EVT_READ)) {
357 		cmn_err(CE_WARN, "ds@%lx: %s: Unexpected LDC event received: "
358 		    "0x%llx" DS_EOL, PORTID(port), __func__,
359 		    (u_longlong_t)event);
360 	}
361 done:
362 	DS_DBG_LDC(CE_NOTE, "ds@%lx: %s: exit" DS_EOL, PORTID(port), __func__);
363 
364 	return (LDC_SUCCESS);
365 }
366 
367 static int
368 ds_ldc_init(ds_port_t *port)
369 {
370 	int		rv;
371 	ldc_attr_t	ldc_attr;
372 	caddr_t		ldc_cb_arg = (caddr_t)port;
373 	char		ebuf[DS_EBUFSIZE];
374 
375 	ASSERT(MUTEX_HELD(&port->lock));
376 
377 	DS_DBG_LDC(CE_NOTE, "ds@%lx: %s: ldc_id=%lld" DS_EOL,
378 	    PORTID(port), __func__, (u_longlong_t)port->ldc.id);
379 
380 	ldc_attr.devclass = LDC_DEV_GENERIC;
381 	ldc_attr.instance = 0;
382 	ldc_attr.mode = LDC_MODE_RELIABLE;
383 	ldc_attr.mtu = DS_STREAM_MTU;
384 
385 	if ((rv = ldc_init(port->ldc.id, &ldc_attr, &port->ldc.hdl)) != 0) {
386 		cmn_err(CE_WARN, "ds@%lx: %s: ldc_id: %lx, ldc_init error: %s"
387 		    DS_EOL, PORTID(port), __func__, port->ldc.id,
388 		    ds_errno_to_str(rv, ebuf));
389 		return (rv);
390 	}
391 
392 	rv = ldc_reg_callback(port->ldc.hdl, ds_ldc_cb, ldc_cb_arg);
393 	if (rv != 0) {
394 		cmn_err(CE_WARN, "ds@%lx: %s: ldc_reg_callback error: %s"
395 		    DS_EOL, PORTID(port), __func__, ds_errno_to_str(rv, ebuf));
396 		return (rv);
397 	}
398 
399 	ds_sys_ldc_init(port);
400 	return (0);
401 }
402 
403 int
404 ds_ldc_fini(ds_port_t *port)
405 {
406 	int	rv;
407 	char	ebuf[DS_EBUFSIZE];
408 
409 	ASSERT(port->state >= DS_PORT_LDC_INIT);
410 	ASSERT(MUTEX_HELD(&port->lock));
411 
412 	DS_DBG_LDC(CE_NOTE, "ds@%lx: %s: ldc_id=%ld" DS_EOL, PORTID(port),
413 	    __func__, port->ldc.id);
414 
415 	if ((rv = ldc_close(port->ldc.hdl)) != 0) {
416 		cmn_err(CE_WARN, "ds@%lx: %s: ldc_close error: %s" DS_EOL,
417 		    PORTID(port), __func__, ds_errno_to_str(rv, ebuf));
418 		return (rv);
419 	}
420 
421 	if ((rv = ldc_unreg_callback(port->ldc.hdl)) != 0) {
422 		cmn_err(CE_WARN, "ds@%lx: %s: ldc_unreg_callback error: %s"
423 		    DS_EOL, PORTID(port), __func__, ds_errno_to_str(rv, ebuf));
424 		return (rv);
425 	}
426 
427 	if ((rv = ldc_fini(port->ldc.hdl)) != 0) {
428 		cmn_err(CE_WARN, "ds@%lx: %s: ldc_fini error: %s" DS_EOL,
429 		    PORTID(port), __func__, ds_errno_to_str(rv, ebuf));
430 		return (rv);
431 	}
432 
433 	port->ldc.id = (uint64_t)-1;
434 	port->ldc.hdl = NULL;
435 	port->ldc.state = 0;
436 
437 	return (rv);
438 }
439 
440 /*
441  * Attempt to read a specified number of bytes from a particular LDC.
442  * Returns zero for success or the return code from the LDC read on
443  * failure. The actual number of bytes read from the LDC is returned
444  * in the size parameter.
445  */
446 static int
447 ds_recv_msg(ds_port_t *port, caddr_t msgp, size_t *sizep)
448 {
449 	int	rv = 0;
450 	size_t	bytes_req = *sizep;
451 	size_t	bytes_left = bytes_req;
452 	size_t	nbytes;
453 	int	retry_count = 0;
454 	char	ebuf[DS_EBUFSIZE];
455 
456 	ASSERT(MUTEX_HELD(&port->rcv_lock));
457 
458 	*sizep = 0;
459 
460 	DS_DBG_LDC(CE_NOTE, "ds@%lx: attempting to read %ld bytes" DS_EOL,
461 	    PORTID(port), bytes_req);
462 
463 	while (bytes_left > 0) {
464 
465 		nbytes = bytes_left;
466 
467 		mutex_enter(&port->lock);
468 		if (port->ldc.state == LDC_UP) {
469 			rv = ldc_read(port->ldc.hdl, msgp, &nbytes);
470 		} else
471 			rv = ENXIO;
472 		mutex_exit(&port->lock);
473 		if (rv != 0) {
474 			if (rv == ECONNRESET) {
475 				break;
476 			} else if (rv != EAGAIN) {
477 				cmn_err(CE_NOTE, "ds@%lx: %s: %s" DS_EOL,
478 				    PORTID(port), __func__,
479 				    ds_errno_to_str(rv, ebuf));
480 				break;
481 			}
482 		} else {
483 			if (nbytes != 0) {
484 				DS_DBG_LDC(CE_NOTE, "ds@%lx: "
485 				    "read %ld bytes, %d retries" DS_EOL,
486 				    PORTID(port), nbytes, retry_count);
487 
488 				*sizep += nbytes;
489 				msgp += nbytes;
490 				bytes_left -= nbytes;
491 
492 				/* reset counter on a successful read */
493 				retry_count = 0;
494 				continue;
495 			}
496 
497 			/*
498 			 * No data was read. Check if this is the
499 			 * first attempt. If so, just return since
500 			 * nothing has been read yet.
501 			 */
502 			if (bytes_left == bytes_req) {
503 				DS_DBG_LDC(CE_NOTE, "ds@%lx: read zero bytes, "
504 				    " no data available" DS_EOL, PORTID(port));
505 				break;
506 			}
507 		}
508 
509 		/*
510 		 * A retry is necessary because the read returned
511 		 * EAGAIN, or a zero length read occurred after
512 		 * reading a partial message.
513 		 */
514 		if (retry_count++ >= ds_retries) {
515 			DS_DBG_LDC(CE_NOTE, "ds@%lx: timed out waiting for "
516 			    "message" DS_EOL, PORTID(port));
517 			break;
518 		}
519 
520 		drv_usecwait(ds_delay);
521 	}
522 
523 	return (rv);
524 }
525 
526 static void
527 ds_handle_recv(void *arg)
528 {
529 	ds_port_t	*port = (ds_port_t *)arg;
530 	char		*hbuf;
531 	size_t		msglen;
532 	size_t		read_size;
533 	boolean_t	hasdata;
534 	ds_hdr_t	hdr;
535 	uint8_t		*msg;
536 	char		*currp;
537 	int		rv;
538 	ds_event_t	*devent;
539 
540 	DS_DBG_LDC(CE_NOTE, "ds@%lx: %s..." DS_EOL, PORTID(port), __func__);
541 
542 	/*
543 	 * Read messages from the channel until there are none
544 	 * pending. Valid messages are dispatched to be handled
545 	 * by a separate thread while any malformed messages are
546 	 * dropped.
547 	 */
548 
549 	mutex_enter(&port->rcv_lock);
550 
551 	for (;;) {
552 		mutex_enter(&port->lock);
553 		if (port->ldc.state == LDC_UP) {
554 			rv = ldc_chkq(port->ldc.hdl, &hasdata);
555 		} else
556 			rv = ENXIO;
557 		mutex_exit(&port->lock);
558 		if (rv != 0 || !hasdata)
559 			break;
560 
561 		DS_DBG(CE_NOTE, "ds@%lx: %s: reading next message" DS_EOL,
562 		    PORTID(port), __func__);
563 
564 		/*
565 		 * Read in the next message.
566 		 */
567 		hbuf = (char *)&hdr;
568 		bzero(hbuf, DS_HDR_SZ);
569 		read_size = DS_HDR_SZ;
570 		currp = hbuf;
571 
572 		/* read in the message header */
573 		if ((rv = ds_recv_msg(port, currp, &read_size)) != 0) {
574 			break;
575 		}
576 
577 		if (read_size < DS_HDR_SZ) {
578 			/*
579 			 * A zero length read is a valid signal that
580 			 * there is no data left on the channel.
581 			 */
582 			if (read_size != 0) {
583 				cmn_err(CE_WARN, "ds@%lx: invalid message "
584 				    "length, received %ld bytes, expected %ld"
585 				    DS_EOL, PORTID(port), read_size, DS_HDR_SZ);
586 			}
587 			continue;
588 		}
589 
590 		/* get payload size and allocate a buffer */
591 		read_size = ((ds_hdr_t *)hbuf)->payload_len;
592 		msglen = DS_HDR_SZ + read_size;
593 		msg = DS_MALLOC(msglen);
594 		if (!msg) {
595 			cmn_err(CE_WARN, "Memory allocation failed attempting "
596 			    " to allocate %d bytes." DS_EOL, (int)msglen);
597 			continue;
598 		}
599 
600 		DS_DBG(CE_NOTE, "ds@%lx: %s: message payload len %d" DS_EOL,
601 		    PORTID(port), __func__, (int)read_size);
602 
603 		/* move message header into buffer */
604 		(void) memcpy(msg, hbuf, DS_HDR_SZ);
605 		currp = (char *)(msg) + DS_HDR_SZ;
606 
607 		/* read in the message body */
608 		if ((rv = ds_recv_msg(port, currp, &read_size)) != 0) {
609 			DS_FREE(msg, msglen);
610 			break;
611 		}
612 
613 		/* validate the size of the message */
614 		if ((DS_HDR_SZ + read_size) != msglen) {
615 			cmn_err(CE_WARN, "ds@%lx: %s: invalid message length, "
616 			    "received %ld bytes, expected %ld" DS_EOL,
617 			    PORTID(port), __func__, (DS_HDR_SZ + read_size),
618 			    msglen);
619 			DS_FREE(msg, msglen);
620 			continue;
621 		}
622 
623 		DS_DUMP_MSG(DS_DBG_FLAG_LDC, msg, msglen);
624 
625 		/*
626 		 * Send the message for processing, and store it
627 		 * in the log. The memory is deallocated only when
628 		 * the message is removed from the log.
629 		 */
630 
631 		devent = DS_MALLOC(sizeof (ds_event_t));
632 		devent->port = port;
633 		devent->buf = (char *)msg;
634 		devent->buflen = msglen;
635 
636 		/* log the message */
637 		(void) ds_log_add_msg(DS_LOG_IN(port->id), msg, msglen);
638 
639 		if (ds_sys_dispatch_func(ds_dispatch_event, devent)) {
640 			cmn_err(CE_WARN, "ds@%lx: error initiating "
641 			    "event handler", PORTID(port));
642 			DS_FREE(devent, sizeof (ds_event_t));
643 		}
644 	}
645 
646 	mutex_exit(&port->rcv_lock);
647 
648 	/* handle connection reset errors returned from ds_recv_msg */
649 	if (rv == ECONNRESET) {
650 		ds_handle_down_reset_events(port);
651 	}
652 
653 	DS_DBG_LDC(CE_NOTE, "ds@%lx: %s done" DS_EOL, PORTID(port), __func__);
654 }
655 
656 static void
657 ds_dispatch_event(void *arg)
658 {
659 	ds_event_t	*event = (ds_event_t *)arg;
660 	ds_hdr_t	*hdr;
661 	ds_port_t	*port;
662 
663 	port = event->port;
664 
665 	hdr = (ds_hdr_t *)event->buf;
666 
667 	if (DS_MSG_TYPE_VALID(hdr->msg_type)) {
668 		DS_DBG(CE_NOTE, "ds@%lx: dispatch_event: msg_type=%d" DS_EOL,
669 		    PORTID(port), hdr->msg_type);
670 
671 		(*ds_msg_handlers[hdr->msg_type])(port, event->buf,
672 		    event->buflen);
673 	} else {
674 		cmn_err(CE_WARN, "ds@%lx: dispatch_event: invalid msg "
675 		    "type (%d)" DS_EOL, PORTID(port), hdr->msg_type);
676 	}
677 
678 	DS_FREE(event->buf, event->buflen);
679 	DS_FREE(event, sizeof (ds_event_t));
680 }
681 
682 int
683 ds_send_msg(ds_port_t *port, caddr_t msg, size_t msglen)
684 {
685 	int	rv;
686 	caddr_t	currp = msg;
687 	size_t	amt_left = msglen;
688 	int	loopcnt = 0;
689 
690 	DS_DBG_LDC(CE_NOTE, "ds@%lx: %s msglen: %ld" DS_EOL, PORTID(port),
691 	    __func__, msglen);
692 	DS_DUMP_MSG(DS_DBG_FLAG_LDC, msg, msglen);
693 
694 	/*
695 	 * Ensure that no other messages can be sent on this port by holding
696 	 * the tx_lock mutex in case the write doesn't get sent with one write.
697 	 * This guarantees that the message doesn't become fragmented.
698 	 */
699 	mutex_enter(&port->tx_lock);
700 
701 	do {
702 		mutex_enter(&port->lock);
703 		if (port->ldc.state == LDC_UP) {
704 			rv = ldc_write(port->ldc.hdl, currp, &msglen);
705 		} else
706 			rv = ENXIO;
707 		mutex_exit(&port->lock);
708 		if (rv != 0) {
709 			if (rv == ECONNRESET) {
710 				mutex_exit(&port->tx_lock);
711 				ds_handle_down_reset_events(port);
712 				return (rv);
713 			} else if ((rv == EWOULDBLOCK) &&
714 			    (loopcnt++ < ds_retries)) {
715 				drv_usecwait(ds_delay);
716 			} else {
717 				cmn_err(CE_WARN, "ds@%lx: send_msg: ldc_write "
718 				    "failed (%d), %d bytes remaining" DS_EOL,
719 				    PORTID(port), rv, (int)amt_left);
720 				goto error;
721 			}
722 		} else {
723 			amt_left -= msglen;
724 			currp += msglen;
725 			msglen = amt_left;
726 			loopcnt = 0;
727 		}
728 	} while (amt_left > 0);
729 error:
730 	mutex_exit(&port->tx_lock);
731 
732 	return (rv);
733 }
734 
735 /* END LDC SUPPORT FUNCTIONS */
736 
737 
738 /* BEGIN DS PROTOCOL SUPPORT FUNCTIONS */
739 
740 static void
741 ds_handle_init_req(ds_port_t *port, caddr_t buf, size_t len)
742 {
743 	ds_hdr_t	*hdr;
744 	ds_init_ack_t	*ack;
745 	ds_init_nack_t	*nack;
746 	char		*msg;
747 	size_t		msglen;
748 	ds_init_req_t	*req;
749 	size_t		explen = DS_MSG_LEN(ds_init_req_t);
750 	uint16_t	new_major;
751 	uint16_t	new_minor;
752 	boolean_t	match;
753 
754 	/* sanity check the incoming message */
755 	if (len != explen) {
756 		cmn_err(CE_WARN, "ds@%lx: <init_req: invalid message "
757 		    "length (%ld), expected %ld" DS_EOL, PORTID(port), len,
758 		    explen);
759 		return;
760 	}
761 
762 	req = (ds_init_req_t *)(buf + DS_HDR_SZ);
763 
764 	DS_DBG_PRCL(CE_NOTE, "ds@%lx: <init_req: ver=%d.%d" DS_EOL,
765 	    PORTID(port), req->major_vers, req->minor_vers);
766 
767 	match = negotiate_version(DS_NUM_VER, &ds_vers[0],
768 	    req->major_vers, &new_major, &new_minor);
769 
770 	/*
771 	 * Check version info. ACK only if the major numbers exactly
772 	 * match. The service entity can retry with a new minor
773 	 * based on the response sent as part of the NACK.
774 	 */
775 	if (match) {
776 		msglen = DS_MSG_LEN(ds_init_ack_t);
777 		msg = DS_MALLOC(msglen);
778 
779 		hdr = (ds_hdr_t *)msg;
780 		hdr->msg_type = DS_INIT_ACK;
781 		hdr->payload_len = sizeof (ds_init_ack_t);
782 
783 		ack = (ds_init_ack_t *)(msg + DS_HDR_SZ);
784 		ack->minor_vers = MIN(new_minor, req->minor_vers);
785 
786 		DS_DBG_PRCL(CE_NOTE, "ds@%lx: init_ack>: minor=0x%04X" DS_EOL,
787 		    PORTID(port), MIN(new_minor, req->minor_vers));
788 	} else {
789 		msglen = DS_MSG_LEN(ds_init_nack_t);
790 		msg = DS_MALLOC(msglen);
791 
792 		hdr = (ds_hdr_t *)msg;
793 		hdr->msg_type = DS_INIT_NACK;
794 		hdr->payload_len = sizeof (ds_init_nack_t);
795 
796 		nack = (ds_init_nack_t *)(msg + DS_HDR_SZ);
797 		nack->major_vers = new_major;
798 
799 		DS_DBG_PRCL(CE_NOTE, "ds@%lx: init_nack>: major=0x%04X" DS_EOL,
800 		    PORTID(port), new_major);
801 	}
802 
803 	/*
804 	 * Send the response
805 	 */
806 	(void) ds_send_msg(port, msg, msglen);
807 	DS_FREE(msg, msglen);
808 }
809 
810 static void
811 ds_handle_init_ack(ds_port_t *port, caddr_t buf, size_t len)
812 {
813 	ds_init_ack_t	*ack;
814 	ds_ver_t	*ver;
815 	size_t		explen = DS_MSG_LEN(ds_init_ack_t);
816 
817 	/* sanity check the incoming message */
818 	if (len != explen) {
819 		cmn_err(CE_WARN, "ds@%lx: <init_ack: invalid message "
820 		    "length (%ld), expected %ld" DS_EOL, PORTID(port), len,
821 		    explen);
822 		return;
823 	}
824 
825 	ack = (ds_init_ack_t *)(buf + DS_HDR_SZ);
826 
827 	mutex_enter(&port->lock);
828 
829 	if (port->state != DS_PORT_INIT_REQ) {
830 		DS_DBG_PRCL(CE_NOTE, "ds@%lx: <init_ack: invalid state: %d"
831 		    DS_EOL, PORTID(port), port->state);
832 		mutex_exit(&port->lock);
833 		return;
834 	}
835 
836 	ver = &(ds_vers[port->ver_idx]);
837 
838 	/* agreed upon a major version */
839 	port->ver.major = ver->major;
840 
841 	/*
842 	 * If the returned minor version is larger than
843 	 * the requested minor version, use the lower of
844 	 * the two, i.e. the requested version.
845 	 */
846 	if (ack->minor_vers >= ver->minor) {
847 		/*
848 		 * Use the minor version specified in the
849 		 * original request.
850 		 */
851 		port->ver.minor = ver->minor;
852 	} else {
853 		/*
854 		 * Use the lower minor version returned in
855 		 * the ack. By definition, all lower minor
856 		 * versions must be supported.
857 		 */
858 		port->ver.minor = ack->minor_vers;
859 	}
860 
861 	port->state = DS_PORT_READY;
862 
863 	DS_DBG_PRCL(CE_NOTE, "ds@%lx: <init_ack: port ready v%d.%d" DS_EOL,
864 	    PORTID(port), port->ver.major, port->ver.minor);
865 
866 	mutex_exit(&port->lock);
867 
868 	/*
869 	 * The port came up, so update all the services
870 	 * with this information. Follow that up with an
871 	 * attempt to register any service that is not
872 	 * already registered.
873 	 */
874 	mutex_enter(&ds_svcs.lock);
875 
876 	(void) ds_walk_svcs(ds_svc_port_up, port);
877 	(void) ds_walk_svcs(ds_svc_register, NULL);
878 
879 	mutex_exit(&ds_svcs.lock);
880 }
881 
882 static void
883 ds_handle_init_nack(ds_port_t *port, caddr_t buf, size_t len)
884 {
885 	int		idx;
886 	ds_init_nack_t	*nack;
887 	ds_ver_t	*ver;
888 	size_t		explen = DS_MSG_LEN(ds_init_nack_t);
889 
890 	/* sanity check the incoming message */
891 	if (len != explen) {
892 		DS_DBG_PRCL(CE_WARN, "ds@%lx: <init_nack: invalid message "
893 		    "length (%ld), expected %ld" DS_EOL, PORTID(port), len,
894 		    explen);
895 		return;
896 	}
897 
898 	nack = (ds_init_nack_t *)(buf + DS_HDR_SZ);
899 
900 	mutex_enter(&port->lock);
901 
902 	if (port->state != DS_PORT_INIT_REQ) {
903 		DS_DBG_PRCL(CE_NOTE, "ds@%lx: <init_nack: invalid state: %d"
904 		    DS_EOL, PORTID(port), port->state);
905 		mutex_exit(&port->lock);
906 		return;
907 	}
908 
909 	ver = &(ds_vers[port->ver_idx]);
910 
911 	DS_DBG_PRCL(CE_NOTE, "ds@%lx: <init_nack: req=v%d.%d, nack=v%d.x"
912 	    DS_EOL, PORTID(port), ver->major, ver->minor, nack->major_vers);
913 
914 	if (nack->major_vers == 0) {
915 		/* no supported protocol version */
916 		DS_DBG_PRCL(CE_WARN, "ds@%lx: <init_nack: DS not supported"
917 		    DS_EOL, PORTID(port));
918 		mutex_exit(&port->lock);
919 		return;
920 	}
921 
922 	/*
923 	 * Walk the version list, looking for a major version
924 	 * that is as close to the requested major version as
925 	 * possible.
926 	 */
927 	for (idx = port->ver_idx; idx < DS_NUM_VER; idx++) {
928 		if (ds_vers[idx].major <= nack->major_vers) {
929 			/* found a version to try */
930 			goto done;
931 		}
932 	}
933 
934 	if (idx == DS_NUM_VER) {
935 		/* no supported version */
936 		DS_DBG_PRCL(CE_WARN, "ds@%lx: <init_nack: DS v%d.x not "
937 		    "supported" DS_EOL, PORTID(port), nack->major_vers);
938 
939 		mutex_exit(&port->lock);
940 		return;
941 	}
942 
943 done:
944 	/* start the handshake again */
945 	port->ver_idx = idx;
946 	port->state = DS_PORT_LDC_INIT;
947 	mutex_exit(&port->lock);
948 
949 	ds_send_init_req(port);
950 
951 }
952 
953 static ds_svc_t *
954 ds_find_svc_by_id_port(char *svc_id, int is_client, ds_port_t *port)
955 {
956 	int		idx;
957 	ds_svc_t	*svc, *found_svc = 0;
958 	uint32_t	flag_match = is_client ? DSSF_ISCLIENT : 0;
959 
960 	ASSERT(MUTEX_HELD(&ds_svcs.lock));
961 
962 	/* walk every table entry */
963 	for (idx = 0; idx < ds_svcs.maxsvcs; idx++) {
964 		svc = ds_svcs.tbl[idx];
965 		if (DS_SVC_ISFREE(svc))
966 			continue;
967 		if (strcmp(svc->cap.svc_id, svc_id) != 0)
968 			continue;
969 		if ((svc->flags & DSSF_ISCLIENT) != flag_match)
970 			continue;
971 		if (port != NULL && svc->port == port) {
972 			return (svc);
973 		} else if (svc->state == DS_SVC_INACTIVE) {
974 			found_svc = svc;
975 		} else if (!found_svc) {
976 			found_svc = svc;
977 		}
978 	}
979 
980 	return (found_svc);
981 }
982 
983 static void
984 ds_handle_reg_req(ds_port_t *port, caddr_t buf, size_t len)
985 {
986 	ds_reg_req_t	*req;
987 	ds_hdr_t	*hdr;
988 	ds_reg_ack_t	*ack;
989 	ds_reg_nack_t	*nack;
990 	char		*msg;
991 	size_t		msglen;
992 	size_t		explen = DS_MSG_LEN(ds_reg_req_t);
993 	ds_svc_t	*svc = NULL;
994 	ds_ver_t	version;
995 	uint16_t	new_major;
996 	uint16_t	new_minor;
997 	boolean_t	match;
998 
999 	/* sanity check the incoming message */
1000 	if (len < explen) {
1001 		cmn_err(CE_WARN, "ds@%lx: <reg_req: invalid message "
1002 		    "length (%ld), expected at least %ld" DS_EOL,
1003 		    PORTID(port), len, explen);
1004 		return;
1005 	}
1006 
1007 	req = (ds_reg_req_t *)(buf + DS_HDR_SZ);
1008 
1009 	DS_DBG_PRCL(CE_NOTE, "ds@%lx: <reg_req: '%s' ver=%d.%d, hdl=0x%llx"
1010 	    DS_EOL, PORTID(port), req->svc_id, req->major_vers, req->minor_vers,
1011 	    (u_longlong_t)req->svc_handle);
1012 
1013 	mutex_enter(&ds_svcs.lock);
1014 	svc = ds_find_svc_by_id_port(req->svc_id,
1015 	    DS_HDL_ISCLIENT(req->svc_handle) == 0, port);
1016 	if (svc == NULL) {
1017 
1018 do_reg_nack:
1019 		mutex_exit(&ds_svcs.lock);
1020 
1021 		msglen = DS_MSG_LEN(ds_reg_nack_t);
1022 		msg = DS_MALLOC(msglen);
1023 
1024 		hdr = (ds_hdr_t *)msg;
1025 		hdr->msg_type = DS_REG_NACK;
1026 		hdr->payload_len = sizeof (ds_reg_nack_t);
1027 
1028 		nack = (ds_reg_nack_t *)(msg + DS_HDR_SZ);
1029 		nack->svc_handle = req->svc_handle;
1030 		nack->result = DS_REG_VER_NACK;
1031 		nack->major_vers = 0;
1032 
1033 		DS_DBG_PRCL(CE_NOTE, "ds@%lx: reg_nack>: '%s'" DS_EOL,
1034 		    PORTID(port), req->svc_id);
1035 		/*
1036 		 * Send the response
1037 		 */
1038 		(void) ds_send_msg(port, msg, msglen);
1039 		DS_FREE(msg, msglen);
1040 		return;
1041 	}
1042 	DS_DBG_PRCL(CE_NOTE, "ds@%lx: <reg_req: '%s' found, hdl: 0x%llx" DS_EOL,
1043 	    PORTID(port), req->svc_id, (u_longlong_t)svc->hdl);
1044 
1045 	/*
1046 	 * A client sends out a reg req in order to force service providers to
1047 	 * initiate a reg req from their end (limitation in the protocol).  We
1048 	 * expect the service provider to be in the inactive (DS_SVC_INACTIVE)
1049 	 * state.  If the service provider has already sent out a reg req (the
1050 	 * state is DS_SVC_REG_PENDING) or has already handshaken (the
1051 	 * state is DS_SVC_ACTIVE), then we can simply ignore this reg
1052 	 * req.  For any other state, we force an unregister before initiating
1053 	 * a reg req.
1054 	 */
1055 
1056 	if (DS_HDL_ISCLIENT(req->svc_handle)) {
1057 		switch (svc->state) {
1058 
1059 		case DS_SVC_REG_PENDING:
1060 		case DS_SVC_ACTIVE:
1061 			DS_DBG_PRCL(CE_NOTE, "ds@%lx: <reg_req: '%s' pinging "
1062 			    "client, state (%x)" DS_EOL, PORTID(port),
1063 			    req->svc_id, svc->state);
1064 			mutex_exit(&ds_svcs.lock);
1065 			return;
1066 
1067 		case DS_SVC_INACTIVE:
1068 			DS_DBG_PRCL(CE_NOTE, "ds@%lx: <reg_req: '%s' pinging "
1069 			    "client" DS_EOL, PORTID(port), req->svc_id);
1070 			break;
1071 
1072 		default:
1073 			DS_DBG_PRCL(CE_NOTE, "ds@%lx: <reg_req: '%s' pinging "
1074 			    "client forced unreg, state (%x)" DS_EOL,
1075 			    PORTID(port), req->svc_id, svc->state);
1076 			(void) ds_svc_unregister(svc, port);
1077 			break;
1078 		}
1079 		(void) ds_svc_port_up(svc, port);
1080 		(void) ds_svc_register_onport(svc, port);
1081 		mutex_exit(&ds_svcs.lock);
1082 		return;
1083 	}
1084 
1085 	/*
1086 	 * Only remote service providers can initiate a registration.  The
1087 	 * local sevice from here must be a client service.
1088 	 */
1089 
1090 	match = negotiate_version(svc->cap.nvers, svc->cap.vers,
1091 	    req->major_vers, &new_major, &new_minor);
1092 
1093 	/*
1094 	 * Check version info. ACK only if the major numbers exactly
1095 	 * match. The service entity can retry with a new minor
1096 	 * based on the response sent as part of the NACK.
1097 	 */
1098 	if (match) {
1099 		DS_DBG_PRCL(CE_NOTE, "ds@%lx: <reg_req: '%s' svc%d: state: %x "
1100 		    "svc_portid: %d" DS_EOL, PORTID(port), req->svc_id,
1101 		    (int)DS_HDL2IDX(svc->hdl), svc->state,
1102 		    (int)(svc->port == NULL ? -1 : PORTID(svc->port)));
1103 		/*
1104 		 * If the current local service is already in use and
1105 		 * it's not on this port, clone it.
1106 		 */
1107 		if (svc->state != DS_SVC_INACTIVE) {
1108 			if (svc->port != NULL && port == svc->port) {
1109 				/*
1110 				 * Someone probably dropped an unreg req
1111 				 * somewhere.  Force a local unreg.
1112 				 */
1113 				(void) ds_svc_unregister(svc, port);
1114 			} else if (!DS_HDL_ISCLIENT(svc->hdl)) {
1115 				/*
1116 				 * Can't clone a non-client (service provider)
1117 				 * handle.  This is because old in-kernel
1118 				 * service providers can't deal with multiple
1119 				 * handles.
1120 				 */
1121 				goto do_reg_nack;
1122 			} else {
1123 				svc = ds_svc_clone(svc);
1124 			}
1125 		}
1126 		svc->port = port;
1127 		svc->svc_hdl = req->svc_handle;
1128 		svc->state = DS_SVC_ACTIVE;
1129 
1130 		msglen = DS_MSG_LEN(ds_reg_ack_t);
1131 		msg = DS_MALLOC(msglen);
1132 
1133 		hdr = (ds_hdr_t *)msg;
1134 		hdr->msg_type = DS_REG_ACK;
1135 		hdr->payload_len = sizeof (ds_reg_ack_t);
1136 
1137 		ack = (ds_reg_ack_t *)(msg + DS_HDR_SZ);
1138 		ack->svc_handle = req->svc_handle;
1139 		ack->minor_vers = MIN(new_minor, req->minor_vers);
1140 
1141 
1142 		if (svc->ops.ds_reg_cb) {
1143 			/* Call the registration callback */
1144 			version.major = req->major_vers;
1145 			version.minor = ack->minor_vers;
1146 			(*svc->ops.ds_reg_cb)(svc->ops.cb_arg, &version,
1147 			    svc->hdl);
1148 		}
1149 		mutex_exit(&ds_svcs.lock);
1150 
1151 		DS_DBG_PRCL(CE_NOTE, "ds@%lx: reg_ack>: '%s' minor=0x%04X"
1152 		    DS_EOL, PORTID(port), svc->cap.svc_id,
1153 		    MIN(new_minor, req->minor_vers));
1154 	} else {
1155 		mutex_exit(&ds_svcs.lock);
1156 
1157 		msglen = DS_MSG_LEN(ds_reg_nack_t);
1158 		msg = DS_MALLOC(msglen);
1159 
1160 		hdr = (ds_hdr_t *)msg;
1161 		hdr->msg_type = DS_REG_NACK;
1162 		hdr->payload_len = sizeof (ds_reg_nack_t);
1163 
1164 		nack = (ds_reg_nack_t *)(msg + DS_HDR_SZ);
1165 		nack->svc_handle = req->svc_handle;
1166 		nack->result = DS_REG_VER_NACK;
1167 		nack->major_vers = new_major;
1168 
1169 		DS_DBG_PRCL(CE_NOTE, "ds@%lx: reg_nack>: '%s' major=0x%04X"
1170 		    DS_EOL, PORTID(port), svc->cap.svc_id, new_major);
1171 	}
1172 
1173 	/* send message */
1174 	(void) ds_send_msg(port, msg, msglen);
1175 	DS_FREE(msg, msglen);
1176 }
1177 
1178 static void
1179 ds_handle_reg_ack(ds_port_t *port, caddr_t buf, size_t len)
1180 {
1181 	ds_reg_ack_t	*ack;
1182 	ds_ver_t	*ver;
1183 	ds_ver_t	tmpver;
1184 	ds_svc_t	*svc;
1185 	size_t		explen = DS_MSG_LEN(ds_reg_ack_t);
1186 
1187 	/* sanity check the incoming message */
1188 	if (len != explen) {
1189 		cmn_err(CE_WARN, "ds@%lx: <reg_ack: invalid message "
1190 		    "length (%ld), expected %ld" DS_EOL, PORTID(port), len,
1191 		    explen);
1192 		return;
1193 	}
1194 
1195 	ack = (ds_reg_ack_t *)(buf + DS_HDR_SZ);
1196 
1197 	mutex_enter(&ds_svcs.lock);
1198 
1199 	/*
1200 	 * This searches for service based on how we generate handles
1201 	 * and so only works because this is a reg ack.
1202 	 */
1203 	if (DS_HDL_ISCLIENT(ack->svc_handle) ||
1204 	    (svc = ds_get_svc(ack->svc_handle)) == NULL) {
1205 		cmn_err(CE_WARN, "ds@%lx: <reg_ack: invalid handle 0x%llx"
1206 		    DS_EOL, PORTID(port), (u_longlong_t)ack->svc_handle);
1207 		goto done;
1208 	}
1209 
1210 	/* make sure the message makes sense */
1211 	if (svc->state != DS_SVC_REG_PENDING) {
1212 		cmn_err(CE_WARN, "ds@%lx: <reg_ack: invalid state (%d)" DS_EOL,
1213 		    PORTID(port), svc->state);
1214 		goto done;
1215 	}
1216 
1217 	ver = &(svc->cap.vers[svc->ver_idx]);
1218 
1219 	/* major version has been agreed upon */
1220 	svc->ver.major = ver->major;
1221 
1222 	if (ack->minor_vers >= ver->minor) {
1223 		/*
1224 		 * Use the minor version specified in the
1225 		 * original request.
1226 		 */
1227 		svc->ver.minor = ver->minor;
1228 	} else {
1229 		/*
1230 		 * Use the lower minor version returned in
1231 		 * the ack. By defninition, all lower minor
1232 		 * versions must be supported.
1233 		 */
1234 		svc->ver.minor = ack->minor_vers;
1235 	}
1236 
1237 	svc->state = DS_SVC_ACTIVE;
1238 	svc->port = port;
1239 
1240 	DS_DBG_PRCL(CE_NOTE, "ds@%lx: <reg_ack: '%s' v%d.%d ready, hdl=0x%llx"
1241 	    DS_EOL, PORTID(port), svc->cap.svc_id, svc->ver.major,
1242 	    svc->ver.minor, (u_longlong_t)svc->hdl);
1243 
1244 	/* notify the client that registration is complete */
1245 	if (svc->ops.ds_reg_cb) {
1246 		/*
1247 		 * Use a temporary version structure so that
1248 		 * the copy in the svc structure cannot be
1249 		 * modified by the client.
1250 		 */
1251 		tmpver.major = svc->ver.major;
1252 		tmpver.minor = svc->ver.minor;
1253 
1254 		(*svc->ops.ds_reg_cb)(svc->ops.cb_arg, &tmpver, svc->hdl);
1255 	}
1256 
1257 done:
1258 	mutex_exit(&ds_svcs.lock);
1259 }
1260 
1261 static void
1262 ds_try_next_port(ds_svc_t *svc, int portid)
1263 {
1264 	ds_port_t *port;
1265 	ds_portset_t totry;
1266 	int i;
1267 
1268 	DS_DBG_LDC(CE_NOTE, "ds@%x %s" DS_EOL, portid, __func__);
1269 
1270 	/*
1271 	 * Get the ports that haven't been tried yet and are available to try.
1272 	 */
1273 	DS_PORTSET_SETNULL(totry);
1274 	for (i = 0; i < DS_MAX_PORTS; i++) {
1275 		if (!DS_PORT_IN_SET(svc->tried, i) &&
1276 		    DS_PORT_IN_SET(svc->avail, i))
1277 			DS_PORTSET_ADD(totry, i);
1278 	}
1279 
1280 	if (DS_PORTSET_ISNULL(totry))
1281 		return;
1282 
1283 	for (i = 0; i < DS_MAX_PORTS; i++, portid++) {
1284 		if (portid >= DS_MAX_PORTS) {
1285 			portid = 0;
1286 		}
1287 
1288 		/*
1289 		 * If the port is not in the available list,
1290 		 * it is not a candidate for registration.
1291 		 */
1292 		if (!DS_PORT_IN_SET(totry, portid)) {
1293 			continue;
1294 		}
1295 
1296 		port = &ds_ports[portid];
1297 		DS_DBG_LDC(CE_NOTE, "ds@%x: %s trying ldc.id: %d" DS_EOL,
1298 		    portid, __func__, (uint_t)(port->ldc.id));
1299 		if (ds_send_reg_req(svc, port) == 0) {
1300 			DS_DBG_LDC(CE_NOTE, "ds@%x: %s reg msg send OK" DS_EOL,
1301 			    portid, __func__);
1302 			/* register sent successfully */
1303 			break;
1304 		}
1305 		DS_DBG_LDC(CE_NOTE, "ds@%x: %s reg msg send FAIL" DS_EOL,
1306 		    portid, __func__);
1307 
1308 		/* reset the service to try the next port */
1309 		ds_reset_svc(svc, port);
1310 	}
1311 }
1312 
1313 static void
1314 ds_handle_reg_nack(ds_port_t *port, caddr_t buf, size_t len)
1315 {
1316 	ds_reg_nack_t	*nack;
1317 	ds_svc_t	*svc;
1318 	int		idx;
1319 	size_t		explen = DS_MSG_LEN(ds_reg_nack_t);
1320 
1321 	/* sanity check the incoming message */
1322 	if (len != explen) {
1323 		cmn_err(CE_WARN, "ds@%lx: <reg_nack: invalid message "
1324 		    "length (%ld), expected %ld" DS_EOL, PORTID(port), len,
1325 		    explen);
1326 		return;
1327 	}
1328 
1329 	nack = (ds_reg_nack_t *)(buf + DS_HDR_SZ);
1330 
1331 	mutex_enter(&ds_svcs.lock);
1332 
1333 	/*
1334 	 * We expect a reg_nack for a client ping.
1335 	 */
1336 	if (DS_HDL_ISCLIENT(nack->svc_handle)) {
1337 		DS_DBG_PRCL(CE_NOTE, "ds@%lx: <reg_nack: ping hdl: 0x%llx"
1338 		    DS_EOL, PORTID(port), (u_longlong_t)nack->svc_handle);
1339 		goto done;
1340 	}
1341 
1342 	/*
1343 	 * This searches for service based on how we generate handles
1344 	 * and so only works because this is a reg nack.
1345 	 */
1346 	if ((svc = ds_get_svc(nack->svc_handle)) == NULL) {
1347 		cmn_err(CE_WARN, "ds@%lx: <reg_nack: invalid handle 0x%llx"
1348 		    DS_EOL, PORTID(port), (u_longlong_t)nack->svc_handle);
1349 		goto done;
1350 	}
1351 
1352 	/* make sure the message makes sense */
1353 	if (svc->state != DS_SVC_REG_PENDING) {
1354 		cmn_err(CE_WARN, "ds@%lx: <reg_nack: invalid state (%d)" DS_EOL,
1355 		    PORTID(port), svc->state);
1356 		goto done;
1357 	}
1358 
1359 	if (nack->result == DS_REG_DUP) {
1360 		cmn_err(CE_WARN, "ds@%lx: <reg_nack: duplicate registration "
1361 		    " for %s" DS_EOL, PORTID(port), svc->cap.svc_id);
1362 		ds_reset_svc(svc, port);
1363 		goto done;
1364 	}
1365 
1366 	/*
1367 	 * A major version of zero indicates that the
1368 	 * service is not supported at all.
1369 	 */
1370 	if (nack->major_vers == 0) {
1371 		DS_DBG_PRCL(CE_NOTE, "ds@%lx: <reg_nack: '%s' not supported"
1372 		    DS_EOL, PORTID(port), svc->cap.svc_id);
1373 		ds_reset_svc(svc, port);
1374 		if ((svc->flags & DSSF_ISCLIENT) == 0)
1375 			ds_try_next_port(svc, PORTID(port) + 1);
1376 		goto done;
1377 	}
1378 
1379 	DS_DBG_PRCL(CE_NOTE, "ds@%lx: <reg_nack: '%s' hdl=0x%llx, nack=%d.x"
1380 	    DS_EOL, PORTID(port), svc->cap.svc_id,
1381 	    (u_longlong_t)nack->svc_handle, nack->major_vers);
1382 
1383 	/*
1384 	 * Walk the version list for the service, looking for
1385 	 * a major version that is as close to the requested
1386 	 * major version as possible.
1387 	 */
1388 	for (idx = svc->ver_idx; idx < svc->cap.nvers; idx++) {
1389 		if (svc->cap.vers[idx].major <= nack->major_vers) {
1390 			/* found a version to try */
1391 			break;
1392 		}
1393 	}
1394 
1395 	if (idx == svc->cap.nvers) {
1396 		/* no supported version */
1397 		DS_DBG_PRCL(CE_NOTE, "ds@%lx: <reg_nack: %s v%d.x not supported"
1398 		    DS_EOL, PORTID(port), svc->cap.svc_id, nack->major_vers);
1399 		ds_reset_svc(svc, port);
1400 		if ((svc->flags & DSSF_ISCLIENT) == 0)
1401 			ds_try_next_port(svc, PORTID(port) + 1);
1402 		goto done;
1403 	}
1404 
1405 	/* start the handshake again */
1406 	svc->state = DS_SVC_INACTIVE;
1407 	svc->ver_idx = idx;
1408 
1409 	(void) ds_svc_register(svc, NULL);
1410 
1411 done:
1412 	mutex_exit(&ds_svcs.lock);
1413 }
1414 
1415 static void
1416 ds_handle_unreg_req(ds_port_t *port, caddr_t buf, size_t len)
1417 {
1418 	ds_hdr_t	*hdr;
1419 	ds_unreg_req_t	*req;
1420 	ds_unreg_ack_t	*ack;
1421 	ds_svc_t	*svc;
1422 	char		*msg;
1423 	size_t		msglen;
1424 	size_t		explen = DS_MSG_LEN(ds_unreg_req_t);
1425 	boolean_t	is_up;
1426 
1427 	/* sanity check the incoming message */
1428 	if (len != explen) {
1429 		cmn_err(CE_WARN, "ds@%lx: <unreg_req: invalid message "
1430 		    "length (%ld), expected %ld" DS_EOL, PORTID(port), len,
1431 		    explen);
1432 		return;
1433 	}
1434 
1435 	req = (ds_unreg_req_t *)(buf + DS_HDR_SZ);
1436 
1437 	mutex_enter(&ds_svcs.lock);
1438 
1439 	/* lookup appropriate client or service */
1440 	if (DS_HDL_ISCLIENT(req->svc_handle) ||
1441 	    ((svc = ds_find_clnt_svc_by_hdl_port(req->svc_handle, port))
1442 	    == NULL && ((svc = ds_get_svc(req->svc_handle)) == NULL ||
1443 	    svc->port != port))) {
1444 		mutex_exit(&ds_svcs.lock);
1445 		mutex_enter(&port->lock);
1446 		is_up = (port->ldc.state == LDC_UP);
1447 		mutex_exit(&port->lock);
1448 		if (!is_up)
1449 			return;
1450 		cmn_err(CE_WARN, "ds@%lx: <unreg_req: invalid handle 0x%llx"
1451 		    DS_EOL, PORTID(port), (u_longlong_t)req->svc_handle);
1452 		ds_send_unreg_nack(port, req->svc_handle);
1453 		return;
1454 	}
1455 
1456 	DS_DBG_PRCL(CE_NOTE, "ds@%lx: <unreg_req: '%s' handle 0x%llx" DS_EOL,
1457 	    PORTID(port), svc->cap.svc_id, (u_longlong_t)req->svc_handle);
1458 
1459 	(void) ds_svc_unregister(svc, svc->port);
1460 
1461 	DS_DBG_PRCL(CE_NOTE, "ds@%lx: unreg_ack>: '%s' hdl=0x%llx" DS_EOL,
1462 	    PORTID(port), svc->cap.svc_id, (u_longlong_t)req->svc_handle);
1463 
1464 	ds_check_for_dup_services(svc);
1465 
1466 	mutex_exit(&ds_svcs.lock);
1467 
1468 	msglen = DS_HDR_SZ + sizeof (ds_unreg_ack_t);
1469 	msg = DS_MALLOC(msglen);
1470 
1471 	hdr = (ds_hdr_t *)msg;
1472 	hdr->msg_type = DS_UNREG_ACK;
1473 	hdr->payload_len = sizeof (ds_unreg_ack_t);
1474 
1475 	ack = (ds_unreg_ack_t *)(msg + DS_HDR_SZ);
1476 	ack->svc_handle = req->svc_handle;
1477 
1478 	/* send message */
1479 	(void) ds_send_msg(port, msg, msglen);
1480 	DS_FREE(msg, msglen);
1481 
1482 }
1483 
1484 static void
1485 ds_handle_unreg_ack(ds_port_t *port, caddr_t buf, size_t len)
1486 {
1487 	ds_unreg_ack_t	*ack;
1488 	size_t		explen = DS_MSG_LEN(ds_unreg_ack_t);
1489 
1490 	/* sanity check the incoming message */
1491 	if (len != explen) {
1492 		cmn_err(CE_WARN, "ds@%lx: <unreg_ack: invalid message "
1493 		    "length (%ld), expected %ld" DS_EOL, PORTID(port), len,
1494 		    explen);
1495 		return;
1496 	}
1497 
1498 	ack = (ds_unreg_ack_t *)(buf + DS_HDR_SZ);
1499 
1500 	DS_DBG_PRCL(CE_NOTE, "ds@%lx: <unreg_ack: hdl=0x%llx" DS_EOL,
1501 	    PORTID(port), (u_longlong_t)ack->svc_handle);
1502 
1503 #ifdef DEBUG
1504 	mutex_enter(&ds_svcs.lock);
1505 
1506 	/*
1507 	 * Since the unregister request was initiated locally,
1508 	 * the service structure has already been torn down.
1509 	 * Just perform a sanity check to make sure the message
1510 	 * is appropriate.
1511 	 */
1512 	if (ds_get_svc(ack->svc_handle) != NULL) {
1513 		DS_DBG_PRCL(CE_NOTE, "ds@%lx: <unreg_ack: handle 0x%llx in use"
1514 		    DS_EOL, PORTID(port), (u_longlong_t)ack->svc_handle);
1515 	}
1516 
1517 	mutex_exit(&ds_svcs.lock);
1518 #endif	/* DEBUG */
1519 }
1520 
1521 static void
1522 ds_handle_unreg_nack(ds_port_t *port, caddr_t buf, size_t len)
1523 {
1524 	ds_unreg_nack_t	*nack;
1525 	size_t		explen = DS_MSG_LEN(ds_unreg_nack_t);
1526 
1527 	/* sanity check the incoming message */
1528 	if (len != explen) {
1529 		cmn_err(CE_WARN, "ds@%lx: <unreg_nack: invalid message "
1530 		    "length (%ld), expected %ld" DS_EOL, PORTID(port), len,
1531 		    explen);
1532 		return;
1533 	}
1534 
1535 	nack = (ds_unreg_nack_t *)(buf + DS_HDR_SZ);
1536 
1537 	DS_DBG_PRCL(CE_NOTE, "ds@%lx: <unreg_nack: hdl=0x%llx" DS_EOL,
1538 	    PORTID(port), (u_longlong_t)nack->svc_handle);
1539 
1540 #ifdef DEBUG
1541 	mutex_enter(&ds_svcs.lock);
1542 
1543 	/*
1544 	 * Since the unregister request was initiated locally,
1545 	 * the service structure has already been torn down.
1546 	 * Just perform a sanity check to make sure the message
1547 	 * is appropriate.
1548 	 */
1549 	if (ds_get_svc(nack->svc_handle) != NULL) {
1550 		DS_DBG_PRCL(CE_NOTE, "ds@%lx: <unreg_nack: handle 0x%llx in use"
1551 		    DS_EOL, PORTID(port), (u_longlong_t)nack->svc_handle);
1552 	}
1553 
1554 	mutex_exit(&ds_svcs.lock);
1555 #endif	/* DEBUG */
1556 }
1557 
1558 static void
1559 ds_handle_data(ds_port_t *port, caddr_t buf, size_t len)
1560 {
1561 	ds_data_handle_t	*data;
1562 	ds_svc_t		*svc;
1563 	char			*msg;
1564 	int			msgsz;
1565 	int			hdrsz;
1566 	size_t			explen = DS_MSG_LEN(ds_data_handle_t);
1567 
1568 	/* sanity check the incoming message */
1569 	if (len < explen) {
1570 		cmn_err(CE_WARN, "ds@%lx: <data: invalid message length "
1571 		    "(%ld), expected at least %ld" DS_EOL, PORTID(port), len,
1572 		    explen);
1573 		return;
1574 	}
1575 
1576 	data = (ds_data_handle_t *)(buf + DS_HDR_SZ);
1577 
1578 	hdrsz = DS_HDR_SZ + sizeof (ds_data_handle_t);
1579 	msgsz = len - hdrsz;
1580 
1581 	/* strip off the header for the client */
1582 	msg = (msgsz) ? (buf + hdrsz) : NULL;
1583 
1584 	mutex_enter(&ds_svcs.lock);
1585 
1586 	if ((svc = ds_find_clnt_svc_by_hdl_port(data->svc_handle, port))
1587 	    == NULL) {
1588 		if ((svc = ds_get_svc(data->svc_handle)) == NULL) {
1589 			mutex_exit(&ds_svcs.lock);
1590 			cmn_err(CE_WARN, "ds@%lx: <data: invalid handle 0x%llx"
1591 			    DS_EOL, PORTID(port),
1592 			    (u_longlong_t)data->svc_handle);
1593 			ds_send_data_nack(port, data->svc_handle);
1594 			return;
1595 		}
1596 	}
1597 
1598 	mutex_exit(&ds_svcs.lock);
1599 
1600 	DS_DBG_PRCL(CE_NOTE, "ds@%lx: <data: '%s' hdl=0x%llx" DS_EOL,
1601 	    PORTID(port), svc->cap.svc_id, (u_longlong_t)svc->hdl);
1602 	DS_DUMP_MSG(DS_DBG_FLAG_PRCL, msg, msgsz);
1603 
1604 	/* dispatch this message to the client */
1605 	(*svc->ops.ds_data_cb)(svc->ops.cb_arg, msg, msgsz);
1606 }
1607 
1608 static void
1609 ds_handle_nack(ds_port_t *port, caddr_t buf, size_t len)
1610 {
1611 	ds_svc_t	*svc;
1612 	ds_data_nack_t	*nack;
1613 	size_t		explen = DS_MSG_LEN(ds_data_nack_t);
1614 
1615 	/* sanity check the incoming message */
1616 	if (len != explen) {
1617 		cmn_err(CE_WARN, "ds@%lx: <data_nack: invalid message "
1618 		    "length (%ld), expected %ld" DS_EOL, PORTID(port), len,
1619 		    explen);
1620 		return;
1621 	}
1622 
1623 	nack = (ds_data_nack_t *)(buf + DS_HDR_SZ);
1624 
1625 	DS_DBG_PRCL(CE_NOTE, "ds@%lx: data_nack: hdl=0x%llx, result=0x%llx"
1626 	    DS_EOL, PORTID(port), (u_longlong_t)nack->svc_handle,
1627 	    (u_longlong_t)nack->result);
1628 
1629 	if (nack->result == DS_INV_HDL) {
1630 
1631 		mutex_enter(&ds_svcs.lock);
1632 
1633 		if ((svc = ds_find_clnt_svc_by_hdl_port(nack->svc_handle,
1634 		    port)) == NULL) {
1635 			if ((svc = ds_get_svc(nack->svc_handle)) == NULL) {
1636 				mutex_exit(&ds_svcs.lock);
1637 				return;
1638 			}
1639 		}
1640 
1641 		cmn_err(CE_WARN, "ds@%lx: <data_nack: handle 0x%llx reported "
1642 		    " as invalid" DS_EOL, PORTID(port),
1643 		    (u_longlong_t)nack->svc_handle);
1644 
1645 		(void) ds_svc_unregister(svc, svc->port);
1646 
1647 		mutex_exit(&ds_svcs.lock);
1648 	}
1649 }
1650 
1651 /* Initialize the port */
1652 void
1653 ds_send_init_req(ds_port_t *port)
1654 {
1655 	ds_hdr_t	*hdr;
1656 	ds_init_req_t	*init_req;
1657 	size_t		msglen;
1658 	ds_ver_t	*vers = &ds_vers[port->ver_idx];
1659 
1660 	mutex_enter(&port->lock);
1661 	if (port->state != DS_PORT_LDC_INIT) {
1662 		DS_DBG_PRCL(CE_NOTE, "ds@%lx: init_req>: invalid state: %d"
1663 		    DS_EOL, PORTID(port), port->state);
1664 		mutex_exit(&port->lock);
1665 		return;
1666 	}
1667 	mutex_exit(&port->lock);
1668 
1669 	DS_DBG_PRCL(CE_NOTE, "ds@%lx: init_req>: req=v%d.%d" DS_EOL,
1670 	    PORTID(port), vers->major, vers->minor);
1671 
1672 	msglen = DS_HDR_SZ + sizeof (ds_init_req_t);
1673 	hdr = DS_MALLOC(msglen);
1674 
1675 	hdr->msg_type = DS_INIT_REQ;
1676 	hdr->payload_len = sizeof (ds_init_req_t);
1677 
1678 	init_req = (ds_init_req_t *)((caddr_t)hdr + DS_HDR_SZ);
1679 	init_req->major_vers = vers->major;
1680 	init_req->minor_vers = vers->minor;
1681 
1682 	if (ds_send_msg(port, (caddr_t)hdr, msglen) == 0) {
1683 		/*
1684 		 * We've left the port state unlocked over the malloc/send,
1685 		 * make sure no one has changed the state under us before
1686 		 * we update the state.
1687 		 */
1688 		mutex_enter(&port->lock);
1689 		if (port->state == DS_PORT_LDC_INIT)
1690 			port->state = DS_PORT_INIT_REQ;
1691 		mutex_exit(&port->lock);
1692 	}
1693 	DS_FREE(hdr, msglen);
1694 }
1695 
1696 static int
1697 ds_send_reg_req(ds_svc_t *svc, ds_port_t *port)
1698 {
1699 	ds_ver_t	*ver;
1700 	ds_hdr_t	*hdr;
1701 	caddr_t		msg;
1702 	size_t		msglen;
1703 	ds_reg_req_t	*req;
1704 	size_t		idlen;
1705 	int		rv;
1706 
1707 	if ((svc->state != DS_SVC_INACTIVE) &&
1708 	    ((svc->flags & DSSF_ISCLIENT) == 0)) {
1709 		DS_DBG_PRCL(CE_NOTE, "ds@%lx: reg_req>: invalid svc state (%d) "
1710 		    "for svc '%s'" DS_EOL, PORTID(port), svc->state,
1711 		    svc->cap.svc_id);
1712 		return (-1);
1713 	}
1714 
1715 	mutex_enter(&port->lock);
1716 
1717 	/* check on the LDC to Zeus */
1718 	if (port->ldc.state != LDC_UP) {
1719 		/* can not send message */
1720 		DS_DBG_PRCL(CE_NOTE, "ds@%lx: reg_req>: channel %ld is not up"
1721 		    DS_EOL, PORTID(port), port->ldc.id);
1722 		mutex_exit(&port->lock);
1723 		return (-1);
1724 	}
1725 
1726 	/* make sure port is ready */
1727 	if (port->state != DS_PORT_READY) {
1728 		/* can not send message */
1729 		DS_DBG_PRCL(CE_NOTE, "ds@%lx: reg_req>: port is not ready"
1730 		    DS_EOL, PORTID(port));
1731 		mutex_exit(&port->lock);
1732 		return (-1);
1733 	}
1734 
1735 	mutex_exit(&port->lock);
1736 
1737 	/* allocate the message buffer */
1738 	idlen = strlen(svc->cap.svc_id);
1739 	msglen = DS_HDR_SZ + sizeof (ds_reg_req_t) + idlen;
1740 	msg = DS_MALLOC(msglen);
1741 
1742 	/* copy in the header data */
1743 	hdr = (ds_hdr_t *)msg;
1744 	hdr->msg_type = DS_REG_REQ;
1745 	hdr->payload_len = sizeof (ds_reg_req_t) + idlen;
1746 
1747 	req = (ds_reg_req_t *)(msg + DS_HDR_SZ);
1748 	req->svc_handle = svc->hdl;
1749 	ver = &(svc->cap.vers[svc->ver_idx]);
1750 	req->major_vers = ver->major;
1751 	req->minor_vers = ver->minor;
1752 
1753 	/* copy in the service id */
1754 	(void) memcpy(req->svc_id, svc->cap.svc_id, idlen + 1);
1755 
1756 	/* send the message */
1757 	DS_DBG_PRCL(CE_NOTE, "ds@%lx: reg_req>: '%s' ver=%d.%d, hdl=0x%llx"
1758 	    DS_EOL, PORTID(port), svc->cap.svc_id, ver->major, ver->minor,
1759 	    (u_longlong_t)svc->hdl);
1760 
1761 	if ((rv = ds_send_msg(port, msg, msglen)) != 0) {
1762 		svc->port = port;
1763 		rv = -1;
1764 	} else if ((svc->flags & DSSF_ISCLIENT) == 0) {
1765 		svc->state = DS_SVC_REG_PENDING;
1766 	}
1767 	DS_FREE(msg, msglen);
1768 
1769 	return (rv);
1770 }
1771 
1772 /*
1773  * Keep around in case we want this later
1774  */
1775 int
1776 ds_send_unreg_req(ds_svc_t *svc)
1777 {
1778 	caddr_t		msg;
1779 	size_t		msglen;
1780 	ds_hdr_t	*hdr;
1781 	ds_unreg_req_t	*req;
1782 	ds_port_t	*port = svc->port;
1783 	int		rv;
1784 
1785 	if (port == NULL) {
1786 		DS_DBG(CE_NOTE, "send_unreg_req: service '%s' not "
1787 		    "associated with a port" DS_EOL, svc->cap.svc_id);
1788 		return (-1);
1789 	}
1790 
1791 	mutex_enter(&port->lock);
1792 
1793 	/* check on the LDC to Zeus */
1794 	if (port->ldc.state != LDC_UP) {
1795 		/* can not send message */
1796 		cmn_err(CE_WARN, "ds@%lx: unreg_req>: channel %ld is not up"
1797 		    DS_EOL, PORTID(port), port->ldc.id);
1798 		mutex_exit(&port->lock);
1799 		return (-1);
1800 	}
1801 
1802 	/* make sure port is ready */
1803 	if (port->state != DS_PORT_READY) {
1804 		/* can not send message */
1805 		cmn_err(CE_WARN, "ds@%lx: unreg_req>: port is not ready" DS_EOL,
1806 		    PORTID(port));
1807 		mutex_exit(&port->lock);
1808 		return (-1);
1809 	}
1810 
1811 	mutex_exit(&port->lock);
1812 
1813 	msglen = DS_HDR_SZ + sizeof (ds_unreg_req_t);
1814 	msg = DS_MALLOC(msglen);
1815 
1816 	/* copy in the header data */
1817 	hdr = (ds_hdr_t *)msg;
1818 	hdr->msg_type = DS_UNREG;
1819 	hdr->payload_len = sizeof (ds_unreg_req_t);
1820 
1821 	req = (ds_unreg_req_t *)(msg + DS_HDR_SZ);
1822 	if (svc->flags & DSSF_ISCLIENT) {
1823 		req->svc_handle = svc->svc_hdl;
1824 	} else {
1825 		req->svc_handle = svc->hdl;
1826 	}
1827 
1828 	/* send the message */
1829 	DS_DBG_PRCL(CE_NOTE, "ds@%lx: unreg_req>: '%s' hdl=0x%llx" DS_EOL,
1830 	    PORTID(port), (svc->cap.svc_id) ? svc->cap.svc_id : "NULL",
1831 	    (u_longlong_t)svc->hdl);
1832 
1833 	if ((rv = ds_send_msg(port, msg, msglen)) != 0) {
1834 		rv = -1;
1835 	}
1836 	DS_FREE(msg, msglen);
1837 
1838 	return (rv);
1839 }
1840 
1841 static void
1842 ds_send_unreg_nack(ds_port_t *port, ds_svc_hdl_t bad_hdl)
1843 {
1844 	caddr_t		msg;
1845 	size_t		msglen;
1846 	ds_hdr_t	*hdr;
1847 	ds_unreg_nack_t	*nack;
1848 
1849 	mutex_enter(&port->lock);
1850 
1851 	/* check on the LDC to Zeus */
1852 	if (port->ldc.state != LDC_UP) {
1853 		/* can not send message */
1854 		cmn_err(CE_WARN, "ds@%lx: unreg_nack>: channel %ld is not up"
1855 		    DS_EOL, PORTID(port), port->ldc.id);
1856 		mutex_exit(&port->lock);
1857 		return;
1858 	}
1859 
1860 	/* make sure port is ready */
1861 	if (port->state != DS_PORT_READY) {
1862 		/* can not send message */
1863 		cmn_err(CE_WARN, "ds@%lx: unreg_nack>: port is not ready"
1864 		    DS_EOL, PORTID(port));
1865 		mutex_exit(&port->lock);
1866 		return;
1867 	}
1868 
1869 	mutex_exit(&port->lock);
1870 
1871 	msglen = DS_HDR_SZ + sizeof (ds_unreg_nack_t);
1872 	msg = DS_MALLOC(msglen);
1873 
1874 	/* copy in the header data */
1875 	hdr = (ds_hdr_t *)msg;
1876 	hdr->msg_type = DS_UNREG_NACK;
1877 	hdr->payload_len = sizeof (ds_unreg_nack_t);
1878 
1879 	nack = (ds_unreg_nack_t *)(msg + DS_HDR_SZ);
1880 	nack->svc_handle = bad_hdl;
1881 
1882 	/* send the message */
1883 	DS_DBG_PRCL(CE_NOTE, "ds@%lx: unreg_nack>: hdl=0x%llx" DS_EOL,
1884 	    PORTID(port), (u_longlong_t)bad_hdl);
1885 
1886 	(void) ds_send_msg(port, msg, msglen);
1887 	DS_FREE(msg, msglen);
1888 }
1889 
1890 static void
1891 ds_send_data_nack(ds_port_t *port, ds_svc_hdl_t bad_hdl)
1892 {
1893 	caddr_t		msg;
1894 	size_t		msglen;
1895 	ds_hdr_t	*hdr;
1896 	ds_data_nack_t	*nack;
1897 
1898 	mutex_enter(&port->lock);
1899 
1900 	/* check on the LDC to Zeus */
1901 	if (port->ldc.state != LDC_UP) {
1902 		/* can not send message */
1903 		cmn_err(CE_WARN, "ds@%lx: data_nack>: channel %ld is not up"
1904 		    DS_EOL, PORTID(port), port->ldc.id);
1905 		mutex_exit(&port->lock);
1906 		return;
1907 	}
1908 
1909 	/* make sure port is ready */
1910 	if (port->state != DS_PORT_READY) {
1911 		/* can not send message */
1912 		cmn_err(CE_WARN, "ds@%lx: data_nack>: port is not ready" DS_EOL,
1913 		    PORTID(port));
1914 		mutex_exit(&port->lock);
1915 		return;
1916 	}
1917 
1918 	mutex_exit(&port->lock);
1919 
1920 	msglen = DS_HDR_SZ + sizeof (ds_data_nack_t);
1921 	msg = DS_MALLOC(msglen);
1922 
1923 	/* copy in the header data */
1924 	hdr = (ds_hdr_t *)msg;
1925 	hdr->msg_type = DS_NACK;
1926 	hdr->payload_len = sizeof (ds_data_nack_t);
1927 
1928 	nack = (ds_data_nack_t *)(msg + DS_HDR_SZ);
1929 	nack->svc_handle = bad_hdl;
1930 	nack->result = DS_INV_HDL;
1931 
1932 	/* send the message */
1933 	DS_DBG_PRCL(CE_NOTE, "ds@%lx: data_nack>: hdl=0x%llx" DS_EOL,
1934 	    PORTID(port), (u_longlong_t)bad_hdl);
1935 
1936 	(void) ds_send_msg(port, msg, msglen);
1937 	DS_FREE(msg, msglen);
1938 }
1939 
1940 /* END DS PROTOCOL SUPPORT FUNCTIONS */
1941 
1942 #ifdef DEBUG
1943 
1944 #define	BYTESPERLINE	8
1945 #define	LINEWIDTH	((BYTESPERLINE * 3) + (BYTESPERLINE + 2) + 1)
1946 #define	ASCIIOFFSET	((BYTESPERLINE * 3) + 2)
1947 #define	ISPRINT(c)	((c >= ' ') && (c <= '~'))
1948 
1949 /*
1950  * Output a buffer formatted with a set number of bytes on
1951  * each line. Append each line with the ASCII equivalent of
1952  * each byte if it falls within the printable ASCII range,
1953  * and '.' otherwise.
1954  */
1955 void
1956 ds_dump_msg(void *vbuf, size_t len)
1957 {
1958 	int	i, j;
1959 	char	*curr;
1960 	char	*aoff;
1961 	char	line[LINEWIDTH];
1962 	uint8_t	*buf = vbuf;
1963 
1964 	if (len > 128)
1965 		len = 128;
1966 
1967 	/* walk the buffer one line at a time */
1968 	for (i = 0; i < len; i += BYTESPERLINE) {
1969 
1970 		bzero(line, LINEWIDTH);
1971 
1972 		curr = line;
1973 		aoff = line + ASCIIOFFSET;
1974 
1975 		/*
1976 		 * Walk the bytes in the current line, storing
1977 		 * the hex value for the byte as well as the
1978 		 * ASCII representation in a temporary buffer.
1979 		 * All ASCII values are placed at the end of
1980 		 * the line.
1981 		 */
1982 		for (j = 0; (j < BYTESPERLINE) && ((i + j) < len); j++) {
1983 			(void) sprintf(curr, " %02x", buf[i + j]);
1984 			*aoff = (ISPRINT(buf[i + j])) ? buf[i + j] : '.';
1985 			curr += 3;
1986 			aoff++;
1987 		}
1988 
1989 		/*
1990 		 * Fill in to the start of the ASCII translation
1991 		 * with spaces. This will only be necessary if
1992 		 * this is the last line and there are not enough
1993 		 * bytes to fill the whole line.
1994 		 */
1995 		while (curr != (line + ASCIIOFFSET))
1996 			*curr++ = ' ';
1997 
1998 		cmn_err(CE_NOTE, "%s" DS_EOL, line);
1999 	}
2000 }
2001 #endif /* DEBUG */
2002 
2003 
2004 /*
2005  * Walk the table of registered services, executing the specified callback
2006  * function for each service on a port. A non-zero return value from the
2007  * callback is used to terminate the walk, not to indicate an error. Returns
2008  * the index of the last service visited.
2009  */
2010 int
2011 ds_walk_svcs(svc_cb_t svc_cb, void *arg)
2012 {
2013 	int		idx;
2014 	ds_svc_t	*svc;
2015 
2016 	ASSERT(MUTEX_HELD(&ds_svcs.lock));
2017 
2018 	/* walk every table entry */
2019 	for (idx = 0; idx < ds_svcs.maxsvcs; idx++) {
2020 		svc = ds_svcs.tbl[idx];
2021 
2022 		/* execute the callback */
2023 		if ((*svc_cb)(svc, arg) != 0)
2024 			break;
2025 	}
2026 
2027 	return (idx);
2028 }
2029 
2030 static int
2031 ds_svc_isfree(ds_svc_t *svc, void *arg)
2032 {
2033 	_NOTE(ARGUNUSED(arg))
2034 
2035 	/*
2036 	 * Looking for a free service. This may be a NULL entry
2037 	 * in the table, or an unused structure that could be
2038 	 * reused.
2039 	 */
2040 
2041 	if (DS_SVC_ISFREE(svc)) {
2042 		/* yes, it is free */
2043 		return (1);
2044 	}
2045 
2046 	/* not a candidate */
2047 	return (0);
2048 }
2049 
2050 int
2051 ds_svc_ismatch(ds_svc_t *svc, void *arg)
2052 {
2053 	if (DS_SVC_ISFREE(svc)) {
2054 		return (0);
2055 	}
2056 
2057 	if (strcmp(svc->cap.svc_id, arg) == 0 &&
2058 	    (svc->flags & DSSF_ISCLIENT) == 0) {
2059 		/* found a match */
2060 		return (1);
2061 	}
2062 
2063 	return (0);
2064 }
2065 
2066 int
2067 ds_svc_clnt_ismatch(ds_svc_t *svc, void *arg)
2068 {
2069 	if (DS_SVC_ISFREE(svc)) {
2070 		return (0);
2071 	}
2072 
2073 	if (strcmp(svc->cap.svc_id, arg) == 0 &&
2074 	    (svc->flags & DSSF_ISCLIENT) != 0) {
2075 		/* found a match */
2076 		return (1);
2077 	}
2078 
2079 	return (0);
2080 }
2081 
2082 int
2083 ds_svc_free(ds_svc_t *svc, void *arg)
2084 {
2085 	_NOTE(ARGUNUSED(arg))
2086 
2087 	if (svc == NULL) {
2088 		return (0);
2089 	}
2090 
2091 	if (svc->cap.svc_id) {
2092 		DS_FREE(svc->cap.svc_id, strlen(svc->cap.svc_id) + 1);
2093 		svc->cap.svc_id = NULL;
2094 	}
2095 
2096 	if (svc->cap.vers) {
2097 		DS_FREE(svc->cap.vers, svc->cap.nvers * sizeof (ds_ver_t));
2098 		svc->cap.vers = NULL;
2099 	}
2100 
2101 	DS_FREE(svc, sizeof (ds_svc_t));
2102 
2103 	return (0);
2104 }
2105 
2106 static int
2107 ds_svc_register_onport(ds_svc_t *svc, ds_port_t *port)
2108 {
2109 	ASSERT(MUTEX_HELD(&ds_svcs.lock));
2110 
2111 	if (DS_SVC_ISFREE(svc))
2112 		return (0);
2113 
2114 	if (!DS_PORT_IN_SET(svc->avail, PORTID(port)))
2115 		return (0);
2116 
2117 	DS_PORTSET_ADD(svc->tried, PORTID(port));
2118 
2119 	if (ds_send_reg_req(svc, port) == 0) {
2120 		/* register sent successfully */
2121 		return (1);
2122 	}
2123 
2124 	if ((svc->flags & DSSF_ISCLIENT) == 0) {
2125 		/* reset the service */
2126 		ds_reset_svc(svc, port);
2127 	}
2128 	return (0);
2129 }
2130 
2131 int
2132 ds_svc_register(ds_svc_t *svc, void *arg)
2133 {
2134 	_NOTE(ARGUNUSED(arg))
2135 	ds_portset_t ports;
2136 	ds_port_t *port;
2137 	int	idx;
2138 
2139 	ASSERT(MUTEX_HELD(&ds_svcs.lock));
2140 
2141 	if (DS_SVC_ISFREE(svc))
2142 		return (0);
2143 
2144 	DS_PORTSET_DUP(ports, svc->avail);
2145 	if (svc->flags & DSSF_ISCLIENT) {
2146 		ds_portset_del_active_clients(svc->cap.svc_id, &ports);
2147 	} else if (svc->state != DS_SVC_INACTIVE)
2148 		return (0);
2149 
2150 	if (DS_PORTSET_ISNULL(ports))
2151 		return (0);
2152 
2153 	/*
2154 	 * Attempt to register the service. Start with the lowest
2155 	 * numbered port and continue until a registration message
2156 	 * is sent successfully, or there are no ports left to try.
2157 	 */
2158 	for (idx = 0; idx < DS_MAX_PORTS; idx++) {
2159 
2160 		/*
2161 		 * If the port is not in the available list,
2162 		 * it is not a candidate for registration.
2163 		 */
2164 		if (!DS_PORT_IN_SET(ports, idx)) {
2165 			continue;
2166 		}
2167 
2168 		port = &ds_ports[idx];
2169 		if (ds_svc_register_onport(svc, port)) {
2170 			if ((svc->flags & DSSF_ISCLIENT) == 0)
2171 				break;
2172 			DS_PORTSET_DEL(svc->avail, idx);
2173 		}
2174 	}
2175 
2176 	return (0);
2177 }
2178 
2179 static int
2180 ds_svc_unregister(ds_svc_t *svc, void *arg)
2181 {
2182 	ds_port_t *port = (ds_port_t *)arg;
2183 	ds_svc_hdl_t hdl;
2184 
2185 	ASSERT(MUTEX_HELD(&ds_svcs.lock));
2186 
2187 	if (DS_SVC_ISFREE(svc)) {
2188 		return (0);
2189 	}
2190 
2191 	/* make sure the service is using this port */
2192 	if (svc->port != port) {
2193 		return (0);
2194 	}
2195 
2196 	if (port) {
2197 		DS_DBG(CE_NOTE, "ds@%lx: svc_unreg: id='%s', ver=%d.%d, "
2198 		    " hdl=0x%09lx" DS_EOL, PORTID(port), svc->cap.svc_id,
2199 		    svc->ver.major, svc->ver.minor, svc->hdl);
2200 	} else {
2201 		DS_DBG(CE_NOTE, "port=NULL: svc_unreg: id='%s', ver=%d.%d, "
2202 		    " hdl=0x%09lx" DS_EOL, svc->cap.svc_id, svc->ver.major,
2203 		    svc->ver.minor, svc->hdl);
2204 	}
2205 
2206 	/* reset the service structure */
2207 	ds_reset_svc(svc, port);
2208 
2209 	/* call the client unregister callback */
2210 	if (svc->ops.ds_unreg_cb) {
2211 		(*svc->ops.ds_unreg_cb)(svc->ops.cb_arg);
2212 	}
2213 
2214 	/* increment the count in the handle to prevent reuse */
2215 	hdl = DS_ALLOC_HDL(DS_HDL2IDX(svc->hdl), DS_HDL2COUNT(svc->hdl));
2216 	if (DS_HDL_ISCLIENT(svc->hdl)) {
2217 		DS_HDL_SET_ISCLIENT(hdl);
2218 	}
2219 	svc->hdl = hdl;
2220 
2221 	if (svc->state != DS_SVC_UNREG_PENDING) {
2222 		/* try to initiate a new registration */
2223 		(void) ds_svc_register(svc, NULL);
2224 	}
2225 
2226 	return (0);
2227 }
2228 
2229 static int
2230 ds_svc_port_up(ds_svc_t *svc, void *arg)
2231 {
2232 	ds_port_t *port = (ds_port_t *)arg;
2233 
2234 	if (DS_SVC_ISFREE(svc)) {
2235 		/* nothing to do */
2236 		return (0);
2237 	}
2238 
2239 	DS_PORTSET_ADD(svc->avail, port->id);
2240 	DS_PORTSET_DEL(svc->tried, port->id);
2241 
2242 	return (0);
2243 }
2244 
2245 ds_svc_t *
2246 ds_alloc_svc(void)
2247 {
2248 	int		idx;
2249 	uint_t		newmaxsvcs;
2250 	ds_svc_t	**newtbl;
2251 	ds_svc_t	*newsvc;
2252 
2253 	ASSERT(MUTEX_HELD(&ds_svcs.lock));
2254 
2255 	idx = ds_walk_svcs(ds_svc_isfree, NULL);
2256 
2257 	if (idx != ds_svcs.maxsvcs) {
2258 		goto found;
2259 	}
2260 
2261 	/*
2262 	 * There was no free space in the table. Grow
2263 	 * the table to double its current size.
2264 	 */
2265 	newmaxsvcs = ds_svcs.maxsvcs * 2;
2266 	newtbl = DS_MALLOC(newmaxsvcs * sizeof (ds_svc_t *));
2267 
2268 	/* copy old table data to the new table */
2269 	(void) memcpy(newtbl, ds_svcs.tbl,
2270 	    ds_svcs.maxsvcs * sizeof (ds_svc_t *));
2271 
2272 	/* clean up the old table */
2273 	DS_FREE(ds_svcs.tbl, ds_svcs.maxsvcs * sizeof (ds_svc_t *));
2274 	ds_svcs.tbl = newtbl;
2275 	ds_svcs.maxsvcs = newmaxsvcs;
2276 
2277 	/* search for a free space again */
2278 	idx = ds_walk_svcs(ds_svc_isfree, NULL);
2279 
2280 	/* the table is locked so should find a free slot */
2281 	ASSERT(idx != ds_svcs.maxsvcs);
2282 
2283 found:
2284 	/* allocate a new svc structure if necessary */
2285 	if ((newsvc = ds_svcs.tbl[idx]) == NULL) {
2286 		/* allocate a new service */
2287 		newsvc = DS_MALLOC(sizeof (ds_svc_t));
2288 		ds_svcs.tbl[idx] = newsvc;
2289 	}
2290 
2291 	/* fill in the handle */
2292 	newsvc->hdl = DS_ALLOC_HDL(idx, DS_HDL2COUNT(newsvc->hdl));
2293 	newsvc->state = DS_SVC_FREE;	/* Mark as free temporarily */
2294 
2295 	return (newsvc);
2296 }
2297 
2298 static void
2299 ds_reset_svc(ds_svc_t *svc, ds_port_t *port)
2300 {
2301 	ASSERT(MUTEX_HELD(&ds_svcs.lock));
2302 
2303 	if (svc->state != DS_SVC_UNREG_PENDING)
2304 		svc->state = DS_SVC_INACTIVE;
2305 	svc->ver_idx = 0;
2306 	svc->ver.major = 0;
2307 	svc->ver.minor = 0;
2308 	svc->port = NULL;
2309 	if (port) {
2310 		DS_PORTSET_DEL(svc->avail, port->id);
2311 	}
2312 }
2313 
2314 ds_svc_t *
2315 ds_get_svc(ds_svc_hdl_t hdl)
2316 {
2317 	int		idx;
2318 	ds_svc_t	*svc;
2319 
2320 	ASSERT(MUTEX_HELD(&ds_svcs.lock));
2321 
2322 	if (hdl == DS_INVALID_HDL)
2323 		return (NULL);
2324 
2325 	idx = DS_HDL2IDX(hdl);
2326 
2327 	/* check if index is out of bounds */
2328 	if ((idx < 0) || (idx >= ds_svcs.maxsvcs))
2329 		return (NULL);
2330 
2331 	svc = ds_svcs.tbl[idx];
2332 
2333 	/* check for a valid service */
2334 	if (DS_SVC_ISFREE(svc))
2335 		return (NULL);
2336 
2337 	/* make sure the handle is an exact match */
2338 	if (svc->hdl != hdl)
2339 		return (NULL);
2340 
2341 	return (svc);
2342 }
2343 
2344 static void
2345 ds_port_reset(ds_port_t *port)
2346 {
2347 	ASSERT(MUTEX_HELD(&ds_svcs.lock));
2348 	ASSERT(MUTEX_HELD(&port->lock));
2349 
2350 	/* connection went down, mark everything inactive */
2351 	(void) ds_walk_svcs(ds_svc_unregister, port);
2352 
2353 	port->ver_idx = 0;
2354 	port->ver.major = 0;
2355 	port->ver.minor = 0;
2356 	port->state = DS_PORT_LDC_INIT;
2357 }
2358 
2359 /*
2360  * Verify that a version array is sorted as expected for the
2361  * version negotiation to work correctly.
2362  */
2363 ds_vers_check_t
2364 ds_vers_isvalid(ds_ver_t *vers, int nvers)
2365 {
2366 	uint16_t	curr_major;
2367 	uint16_t	curr_minor;
2368 	int		idx;
2369 
2370 	curr_major = vers[0].major;
2371 	curr_minor = vers[0].minor;
2372 
2373 	/*
2374 	 * Walk the version array, verifying correct ordering.
2375 	 * The array must be sorted from highest supported
2376 	 * version to lowest supported version.
2377 	 */
2378 	for (idx = 0; idx < nvers; idx++) {
2379 		if (vers[idx].major > curr_major) {
2380 			DS_DBG(CE_NOTE, "ds_vers_isvalid: version array has "
2381 			    " increasing major versions" DS_EOL);
2382 			return (DS_VERS_INCREASING_MAJOR_ERR);
2383 		}
2384 
2385 		if (vers[idx].major < curr_major) {
2386 			curr_major = vers[idx].major;
2387 			curr_minor = vers[idx].minor;
2388 			continue;
2389 		}
2390 
2391 		if (vers[idx].minor > curr_minor) {
2392 			DS_DBG(CE_NOTE, "ds_vers_isvalid: version array has "
2393 			    " increasing minor versions" DS_EOL);
2394 			return (DS_VERS_INCREASING_MINOR_ERR);
2395 		}
2396 
2397 		curr_minor = vers[idx].minor;
2398 	}
2399 
2400 	return (DS_VERS_OK);
2401 }
2402 
2403 /*
2404  * Extended user capability init.
2405  */
2406 int
2407 ds_ucap_init(ds_capability_t *cap, ds_clnt_ops_t *ops, uint32_t flags,
2408     int instance, ds_svc_hdl_t *hdlp)
2409 {
2410 	ds_vers_check_t	status;
2411 	ds_svc_t	*svc;
2412 	int		rv = 0;
2413 	ds_svc_hdl_t 	lb_hdl, hdl;
2414 	int		is_loopback;
2415 	int		is_client;
2416 
2417 	/* sanity check the args */
2418 	if ((cap == NULL) || (ops == NULL)) {
2419 		cmn_err(CE_NOTE, "%s: invalid arguments" DS_EOL, __func__);
2420 		return (EINVAL);
2421 	}
2422 
2423 	/* sanity check the capability specifier */
2424 	if ((cap->svc_id == NULL) || (cap->vers == NULL) || (cap->nvers == 0)) {
2425 		cmn_err(CE_NOTE, "%s: invalid capability specifier" DS_EOL,
2426 		    __func__);
2427 		return (EINVAL);
2428 	}
2429 
2430 	/* sanity check the version array */
2431 	if ((status = ds_vers_isvalid(cap->vers, cap->nvers)) != DS_VERS_OK) {
2432 		cmn_err(CE_NOTE, "%s: invalid capability version array "
2433 		    "for %s service: %s" DS_EOL, __func__, cap->svc_id,
2434 		    (status == DS_VERS_INCREASING_MAJOR_ERR) ?
2435 		    "increasing major versions" :
2436 		    "increasing minor versions");
2437 		return (EINVAL);
2438 	}
2439 
2440 	/* data and register callbacks are required */
2441 	if ((ops->ds_data_cb == NULL) || (ops->ds_reg_cb == NULL)) {
2442 		cmn_err(CE_NOTE, "%s: invalid ops specifier for %s service"
2443 		    DS_EOL, __func__, cap->svc_id);
2444 		return (EINVAL);
2445 	}
2446 
2447 	flags &= DSSF_USERFLAGS;
2448 	is_client = flags & DSSF_ISCLIENT;
2449 
2450 	DS_DBG_USR(CE_NOTE, "%s: svc_id='%s', data_cb=0x%lx, cb_arg=0x%lx"
2451 	    DS_EOL, __func__, cap->svc_id, PTR_TO_LONG(ops->ds_data_cb),
2452 	    PTR_TO_LONG(ops->cb_arg));
2453 
2454 	mutex_enter(&ds_svcs.lock);
2455 
2456 	/* check if the service is already registered */
2457 	if (i_ds_hdl_lookup(cap->svc_id, is_client, NULL, 1) == 1) {
2458 		/* already registered */
2459 		cmn_err(CE_NOTE, "Service '%s'/%s already registered" DS_EOL,
2460 		    cap->svc_id,
2461 		    (flags & DSSF_ISCLIENT) ? "client" : "service");
2462 		mutex_exit(&ds_svcs.lock);
2463 		return (EALREADY);
2464 	}
2465 
2466 	svc = ds_alloc_svc();
2467 	if (is_client) {
2468 		DS_HDL_SET_ISCLIENT(svc->hdl);
2469 	}
2470 
2471 	svc->state = DS_SVC_FREE;
2472 	svc->svc_hdl = DS_BADHDL1;
2473 
2474 	svc->flags = flags;
2475 	svc->drvi = instance;
2476 	svc->drv_psp = NULL;
2477 
2478 	/*
2479 	 * Check for loopback.  "pri" is a legacy service that assumes it
2480 	 * will never use loopback mode.
2481 	 */
2482 	if (strcmp(cap->svc_id, "pri") == 0) {
2483 		is_loopback = 0;
2484 	} else if (i_ds_hdl_lookup(cap->svc_id, is_client == 0, &lb_hdl, 1)
2485 	    == 1) {
2486 		if ((rv = ds_loopback_set_svc(svc, cap, &lb_hdl)) != 0) {
2487 			DS_DBG_USR(CE_NOTE, "%s: ds_loopback_set_svc '%s' err "
2488 			    " (%d)" DS_EOL, __func__, cap->svc_id, rv);
2489 			mutex_exit(&ds_svcs.lock);
2490 			return (rv);
2491 		}
2492 		is_loopback = 1;
2493 	} else
2494 		is_loopback = 0;
2495 
2496 	/* copy over all the client information */
2497 	(void) memcpy(&svc->cap, cap, sizeof (ds_capability_t));
2498 
2499 	/* make a copy of the service name */
2500 	svc->cap.svc_id = ds_strdup(cap->svc_id);
2501 
2502 	/* make a copy of the version array */
2503 	svc->cap.vers = DS_MALLOC(cap->nvers * sizeof (ds_ver_t));
2504 	(void) memcpy(svc->cap.vers, cap->vers, cap->nvers * sizeof (ds_ver_t));
2505 
2506 	/* copy the client ops vector */
2507 	(void) memcpy(&svc->ops, ops, sizeof (ds_clnt_ops_t));
2508 
2509 	svc->state = DS_SVC_INACTIVE;
2510 	svc->ver_idx = 0;
2511 	DS_PORTSET_DUP(svc->avail, ds_allports);
2512 	DS_PORTSET_SETNULL(svc->tried);
2513 
2514 	ds_svcs.nsvcs++;
2515 
2516 	hdl = svc->hdl;
2517 
2518 	/*
2519 	 * kludge to allow user callback code to get handle and user args.
2520 	 * Make sure the callback arg points to the svc structure.
2521 	 */
2522 	if ((flags & DSSF_ISUSER) != 0) {
2523 		ds_cbarg_set_cookie(svc);
2524 	}
2525 
2526 	if (is_loopback) {
2527 		ds_loopback_register(hdl);
2528 		ds_loopback_register(lb_hdl);
2529 	}
2530 
2531 	/*
2532 	 * If this is a client or a non-loopback service provider, send
2533 	 * out register requests.
2534 	 */
2535 	if (!is_loopback || (flags & DSSF_ISCLIENT) != 0)
2536 		(void) ds_svc_register(svc, NULL);
2537 
2538 	if (hdlp) {
2539 		*hdlp = hdl;
2540 	}
2541 
2542 	mutex_exit(&ds_svcs.lock);
2543 
2544 	DS_DBG_USR(CE_NOTE, "%s: service '%s' assigned handle 0x%09lx" DS_EOL,
2545 	    __func__, svc->cap.svc_id, hdl);
2546 
2547 	return (0);
2548 }
2549 
2550 /*
2551  * ds_cap_init interface for previous revision.
2552  */
2553 int
2554 ds_cap_init(ds_capability_t *cap, ds_clnt_ops_t *ops)
2555 {
2556 	return (ds_ucap_init(cap, ops, 0, DS_INVALID_INSTANCE, NULL));
2557 }
2558 
2559 /*
2560  * Interface for ds_unreg_hdl in lds driver.
2561  */
2562 int
2563 ds_unreg_hdl(ds_svc_hdl_t hdl)
2564 {
2565 	ds_svc_t	*svc;
2566 	int		is_loopback;
2567 	ds_svc_hdl_t	lb_hdl;
2568 
2569 	DS_DBG_USR(CE_NOTE, "%s: hdl=0x%09lx" DS_EOL, __func__, hdl);
2570 
2571 	mutex_enter(&ds_svcs.lock);
2572 	if ((svc = ds_get_svc(hdl)) == NULL) {
2573 		mutex_exit(&ds_svcs.lock);
2574 		DS_DBG_USR(CE_NOTE, "%s: unknown hdl: 0x%llx" DS_EOL, __func__,
2575 		    (u_longlong_t)hdl);
2576 		return (ENXIO);
2577 	}
2578 
2579 	DS_DBG_USR(CE_NOTE, "%s: svcid='%s', hdl=0x%llx" DS_EOL, __func__,
2580 	    svc->cap.svc_id, (u_longlong_t)svc->hdl);
2581 
2582 	svc->state = DS_SVC_UNREG_PENDING;
2583 
2584 	is_loopback = ((svc->flags & DSSF_LOOPBACK) != 0);
2585 	lb_hdl = svc->svc_hdl;
2586 
2587 	if (svc->port) {
2588 		(void) ds_send_unreg_req(svc);
2589 	}
2590 
2591 	(void) ds_svc_unregister(svc, svc->port);
2592 
2593 	ds_delete_svc_entry(svc);
2594 
2595 	if (is_loopback) {
2596 		ds_loopback_unregister(lb_hdl);
2597 	}
2598 
2599 	mutex_exit(&ds_svcs.lock);
2600 
2601 	return (0);
2602 }
2603 
2604 int
2605 ds_cap_fini(ds_capability_t *cap)
2606 {
2607 	ds_svc_hdl_t	hdl;
2608 	int rv;
2609 	uint_t nhdls = 0;
2610 
2611 	DS_DBG(CE_NOTE, "%s: '%s'" DS_EOL, __func__, cap->svc_id);
2612 	if ((rv = ds_hdl_lookup(cap->svc_id, 0, &hdl, 1, &nhdls)) != 0) {
2613 		DS_DBG(CE_NOTE, "%s: ds_hdl_lookup '%s' err (%d)" DS_EOL,
2614 		    __func__, cap->svc_id, rv);
2615 		return (rv);
2616 	}
2617 
2618 	if (nhdls == 0) {
2619 		DS_DBG(CE_NOTE, "%s: no such service '%s'" DS_EOL,
2620 		    __func__, cap->svc_id);
2621 		return (ENXIO);
2622 	}
2623 
2624 	if ((rv = ds_is_my_hdl(hdl, DS_INVALID_INSTANCE)) != 0) {
2625 		DS_DBG(CE_NOTE, "%s: ds_is_my_handle err (%d)" DS_EOL, __func__,
2626 		    rv);
2627 		return (rv);
2628 	}
2629 
2630 	if ((rv = ds_unreg_hdl(hdl)) != 0) {
2631 		DS_DBG(CE_NOTE, "%s: ds_unreg_hdl err (%d)" DS_EOL, __func__,
2632 		    rv);
2633 		return (rv);
2634 	}
2635 
2636 	return (0);
2637 }
2638 
2639 int
2640 ds_cap_send(ds_svc_hdl_t hdl, void *buf, size_t len)
2641 {
2642 	int		rv;
2643 	ds_hdr_t	*hdr;
2644 	caddr_t		msg;
2645 	size_t		msglen;
2646 	size_t		hdrlen;
2647 	caddr_t		payload;
2648 	ds_svc_t	*svc;
2649 	ds_port_t	*port;
2650 	ds_data_handle_t *data;
2651 	ds_svc_hdl_t	svc_hdl;
2652 	int		is_client = 0;
2653 
2654 	DS_DBG(CE_NOTE, "%s: hdl: 0x%llx, buf: %lx, len: %ld" DS_EOL, __func__,
2655 	    (u_longlong_t)hdl, (ulong_t)buf, len);
2656 
2657 	mutex_enter(&ds_svcs.lock);
2658 
2659 	if ((svc = ds_get_svc(hdl)) == NULL) {
2660 		cmn_err(CE_WARN, "%s: invalid handle 0x%llx" DS_EOL, __func__,
2661 		    (u_longlong_t)hdl);
2662 		mutex_exit(&ds_svcs.lock);
2663 		return (ENXIO);
2664 	}
2665 
2666 	if (svc->state != DS_SVC_ACTIVE) {
2667 		/* channel is up, but svc is not registered */
2668 		DS_DBG(CE_NOTE, "%s: invalid service state 0x%x" DS_EOL,
2669 		    __func__, svc->state);
2670 		mutex_exit(&ds_svcs.lock);
2671 		return (ENOTCONN);
2672 	}
2673 
2674 	if (svc->flags & DSSF_LOOPBACK) {
2675 		hdl = svc->svc_hdl;
2676 		mutex_exit(&ds_svcs.lock);
2677 		ds_loopback_send(hdl, buf, len);
2678 		return (0);
2679 	}
2680 
2681 	if ((port = svc->port) == NULL) {
2682 		DS_DBG(CE_NOTE, "%s: service '%s' not associated with a port"
2683 		    DS_EOL, __func__, svc->cap.svc_id);
2684 		mutex_exit(&ds_svcs.lock);
2685 		return (ECONNRESET);
2686 	}
2687 
2688 	if (svc->flags & DSSF_ISCLIENT) {
2689 		is_client = 1;
2690 		svc_hdl = svc->svc_hdl;
2691 	}
2692 
2693 	mutex_exit(&ds_svcs.lock);
2694 
2695 	/* check that the LDC channel is ready */
2696 	if (port->ldc.state != LDC_UP) {
2697 		DS_DBG(CE_NOTE, "%s: LDC channel is not up" DS_EOL, __func__);
2698 		return (ECONNRESET);
2699 	}
2700 
2701 	hdrlen = DS_HDR_SZ + sizeof (ds_data_handle_t);
2702 
2703 	msg = DS_MALLOC(len + hdrlen);
2704 	hdr = (ds_hdr_t *)msg;
2705 	payload = msg + hdrlen;
2706 	msglen = len + hdrlen;
2707 
2708 	hdr->payload_len = len + sizeof (ds_data_handle_t);
2709 	hdr->msg_type = DS_DATA;
2710 
2711 	data = (ds_data_handle_t *)(msg + DS_HDR_SZ);
2712 	if (is_client) {
2713 		data->svc_handle = svc_hdl;
2714 	} else {
2715 		data->svc_handle = hdl;
2716 	}
2717 
2718 	if ((buf != NULL) && (len != 0)) {
2719 		(void) memcpy(payload, buf, len);
2720 	}
2721 
2722 	DS_DBG_PRCL(CE_NOTE, "ds@%lx: data>: hdl=0x%llx, len=%ld, "
2723 	    " payload_len=%d" DS_EOL, PORTID(port), (u_longlong_t)svc->hdl,
2724 	    msglen, hdr->payload_len);
2725 	DS_DUMP_MSG(DS_DBG_FLAG_PRCL, msg, msglen);
2726 
2727 	if ((rv = ds_send_msg(port, msg, msglen)) != 0) {
2728 		rv = (rv == EIO) ? ECONNRESET : rv;
2729 	}
2730 	DS_FREE(msg, msglen);
2731 
2732 	return (rv);
2733 }
2734 
2735 void
2736 ds_port_common_init(ds_port_t *port)
2737 {
2738 	int rv;
2739 
2740 	if ((port->flags & DS_PORT_MUTEX_INITED) == 0) {
2741 		mutex_init(&port->lock, NULL, MUTEX_DRIVER, NULL);
2742 		mutex_init(&port->tx_lock, NULL, MUTEX_DRIVER, NULL);
2743 		mutex_init(&port->rcv_lock, NULL, MUTEX_DRIVER, NULL);
2744 		port->flags |= DS_PORT_MUTEX_INITED;
2745 	}
2746 
2747 	port->state = DS_PORT_INIT;
2748 	DS_PORTSET_ADD(ds_allports, port->id);
2749 
2750 	ds_sys_port_init(port);
2751 
2752 	mutex_enter(&port->lock);
2753 	rv = ds_ldc_init(port);
2754 	mutex_exit(&port->lock);
2755 
2756 	/*
2757 	 * If LDC successfully init'ed, try to kick off protocol for this port.
2758 	 */
2759 	if (rv == 0) {
2760 		ds_handle_up_event(port);
2761 	}
2762 }
2763 
2764 void
2765 ds_port_common_fini(ds_port_t *port)
2766 {
2767 	ASSERT(MUTEX_HELD(&port->lock));
2768 
2769 	port->state = DS_PORT_FREE;
2770 
2771 	DS_PORTSET_DEL(ds_allports, port->id);
2772 
2773 	ds_sys_port_fini(port);
2774 }
2775 
2776 /*
2777  * Initialize table of registered service classes
2778  */
2779 void
2780 ds_init_svcs_tbl(uint_t nentries)
2781 {
2782 	int	tblsz;
2783 
2784 	ds_svcs.maxsvcs = nentries;
2785 
2786 	tblsz = ds_svcs.maxsvcs * sizeof (ds_svc_t *);
2787 	ds_svcs.tbl = (ds_svc_t **)DS_MALLOC(tblsz);
2788 
2789 	ds_svcs.nsvcs = 0;
2790 }
2791 
2792 /*
2793  * Find the max and min version supported.
2794  * Hacked from zeus workspace, support.c
2795  */
2796 static void
2797 min_max_versions(int num_versions, ds_ver_t *sup_versionsp,
2798     uint16_t *min_major, uint16_t *max_major)
2799 {
2800 	int i;
2801 
2802 	*min_major = sup_versionsp[0].major;
2803 	*max_major = *min_major;
2804 
2805 	for (i = 1; i < num_versions; i++) {
2806 		if (sup_versionsp[i].major < *min_major)
2807 			*min_major = sup_versionsp[i].major;
2808 
2809 		if (sup_versionsp[i].major > *max_major)
2810 			*max_major = sup_versionsp[i].major;
2811 	}
2812 }
2813 
2814 /*
2815  * Check whether the major and minor numbers requested by the peer can be
2816  * satisfied. If the requested major is supported, true is returned, and the
2817  * agreed minor is returned in new_minor. If the requested major is not
2818  * supported, the routine returns false, and the closest major is returned in
2819  * *new_major, upon which the peer should re-negotiate. The closest major is
2820  * the just lower that the requested major number.
2821  *
2822  * Hacked from zeus workspace, support.c
2823  */
2824 boolean_t
2825 negotiate_version(int num_versions, ds_ver_t *sup_versionsp,
2826     uint16_t req_major, uint16_t *new_majorp, uint16_t *new_minorp)
2827 {
2828 	int i;
2829 	uint16_t major, lower_major;
2830 	uint16_t min_major = 0, max_major;
2831 	boolean_t found_match = B_FALSE;
2832 
2833 	min_max_versions(num_versions, sup_versionsp, &min_major, &max_major);
2834 
2835 	DS_DBG(CE_NOTE, "negotiate_version: req_major = %u, min = %u, max = %u"
2836 	    DS_EOL, req_major, min_major, max_major);
2837 
2838 	/*
2839 	 * If the minimum version supported is greater than
2840 	 * the version requested, return the lowest version
2841 	 * supported
2842 	 */
2843 	if (min_major > req_major) {
2844 		*new_majorp = min_major;
2845 		return (B_FALSE);
2846 	}
2847 
2848 	/*
2849 	 * If the largest version supported is lower than
2850 	 * the version requested, return the largest version
2851 	 * supported
2852 	 */
2853 	if (max_major < req_major) {
2854 		*new_majorp = max_major;
2855 		return (B_FALSE);
2856 	}
2857 
2858 	/*
2859 	 * Now we know that the requested version lies between the
2860 	 * min and max versions supported. Check if the requested
2861 	 * major can be found in supported versions.
2862 	 */
2863 	lower_major = min_major;
2864 	for (i = 0; i < num_versions; i++) {
2865 		major = sup_versionsp[i].major;
2866 		if (major == req_major) {
2867 			found_match = B_TRUE;
2868 			*new_majorp = req_major;
2869 			*new_minorp = sup_versionsp[i].minor;
2870 			break;
2871 		} else {
2872 			if ((major < req_major) && (major > lower_major))
2873 				lower_major = major;
2874 		}
2875 	}
2876 
2877 	/*
2878 	 * If no match is found, return the closest available number
2879 	 */
2880 	if (!found_match)
2881 		*new_majorp = lower_major;
2882 
2883 	return (found_match);
2884 }
2885 
2886 /*
2887  * Specific errno's that are used by ds.c and ldc.c
2888  */
2889 static struct {
2890 	int ds_errno;
2891 	char *estr;
2892 } ds_errno_to_str_tab[] = {
2893 	{ EIO,		"I/O error" },
2894 	{ ENXIO,	"No such device or address" },
2895 	{ EAGAIN,	"Resource temporarily unavailable" },
2896 	{ ENOMEM,	"Not enough space" },
2897 	{ EACCES,	"Permission denied" },
2898 	{ EFAULT,	"Bad address" },
2899 	{ EBUSY,	"Device busy" },
2900 	{ EINVAL,	"Invalid argument" },
2901 	{ ENOSPC,	"No space left on device" },
2902 	{ ENOMSG,	"No message of desired type" },
2903 #ifdef	ECHRNG
2904 	{ ECHRNG,	"Channel number out of range" },
2905 #endif
2906 	{ ENOTSUP,	"Operation not supported" },
2907 	{ EMSGSIZE,	"Message too long" },
2908 	{ EADDRINUSE,	"Address already in use" },
2909 	{ ECONNRESET,	"Connection reset by peer" },
2910 	{ ENOBUFS,	"No buffer space available" },
2911 	{ ENOTCONN,	"Socket is not connected" },
2912 	{ ECONNREFUSED,	"Connection refused" },
2913 	{ EALREADY,	"Operation already in progress" },
2914 	{ 0,		NULL },
2915 };
2916 
2917 char *
2918 ds_errno_to_str(int ds_errno, char *ebuf)
2919 {
2920 	int i, en;
2921 
2922 	for (i = 0; (en = ds_errno_to_str_tab[i].ds_errno) != 0; i++) {
2923 		if (en == ds_errno) {
2924 			(void) strcpy(ebuf, ds_errno_to_str_tab[i].estr);
2925 			return (ebuf);
2926 		}
2927 	}
2928 
2929 	(void) sprintf(ebuf, "ds_errno (%d)", ds_errno);
2930 	return (ebuf);
2931 }
2932 
2933 static void
2934 ds_loopback_register(ds_svc_hdl_t hdl)
2935 {
2936 	ds_ver_t ds_ver;
2937 	ds_svc_t *svc;
2938 
2939 	ASSERT(MUTEX_HELD(&ds_svcs.lock));
2940 	DS_DBG_LOOP(CE_NOTE, "%s: entered hdl: 0x%llx" DS_EOL, __func__,
2941 	    (u_longlong_t)hdl);
2942 	if ((svc = ds_get_svc(hdl)) == NULL) {
2943 		DS_DBG_LOOP(CE_NOTE, "%s: invalid hdl: 0x%llx" DS_EOL, __func__,
2944 		    (u_longlong_t)hdl);
2945 		return;
2946 	}
2947 
2948 	svc->state = DS_SVC_ACTIVE;
2949 
2950 	if (svc->ops.ds_reg_cb) {
2951 		DS_DBG_LOOP(CE_NOTE, "%s: loopback regcb: hdl: 0x%llx" DS_EOL,
2952 		    __func__, (u_longlong_t)hdl);
2953 		ds_ver.major = svc->ver.major;
2954 		ds_ver.minor = svc->ver.minor;
2955 		(*svc->ops.ds_reg_cb)(svc->ops.cb_arg, &ds_ver, hdl);
2956 	}
2957 }
2958 
2959 static void
2960 ds_loopback_unregister(ds_svc_hdl_t hdl)
2961 {
2962 	ds_svc_t *svc;
2963 
2964 	ASSERT(MUTEX_HELD(&ds_svcs.lock));
2965 	if ((svc = ds_get_svc(hdl)) == NULL) {
2966 		DS_DBG_LOOP(CE_NOTE, "%s: invalid hdl: 0x%llx" DS_EOL, __func__,
2967 		    (u_longlong_t)hdl);
2968 		return;
2969 	}
2970 
2971 	DS_DBG_LOOP(CE_NOTE, "%s: entered hdl: 0x%llx" DS_EOL, __func__,
2972 	    (u_longlong_t)hdl);
2973 
2974 	svc->flags &= ~DSSF_LOOPBACK;
2975 	svc->svc_hdl = DS_BADHDL2;
2976 	svc->state = DS_SVC_INACTIVE;
2977 
2978 	if (svc->ops.ds_unreg_cb) {
2979 		DS_DBG_LOOP(CE_NOTE, "%s: loopback unregcb: hdl: 0x%llx" DS_EOL,
2980 		    __func__, (u_longlong_t)hdl);
2981 		(*svc->ops.ds_unreg_cb)(svc->ops.cb_arg);
2982 	}
2983 }
2984 
2985 static void
2986 ds_loopback_send(ds_svc_hdl_t hdl, void *buf, size_t buflen)
2987 {
2988 	ds_svc_t *svc;
2989 
2990 	mutex_enter(&ds_svcs.lock);
2991 	if ((svc = ds_get_svc(hdl)) == NULL) {
2992 		mutex_exit(&ds_svcs.lock);
2993 		DS_DBG_LOOP(CE_NOTE, "%s: invalid hdl: 0x%llx" DS_EOL, __func__,
2994 		    (u_longlong_t)hdl);
2995 		return;
2996 	}
2997 	mutex_exit(&ds_svcs.lock);
2998 
2999 	DS_DBG_LOOP(CE_NOTE, "%s: entered hdl: 0x%llx" DS_EOL, __func__,
3000 	    (u_longlong_t)hdl);
3001 
3002 	if (svc->ops.ds_data_cb) {
3003 		DS_DBG_LOOP(CE_NOTE, "%s: loopback datacb hdl: 0x%llx" DS_EOL,
3004 		    __func__, (u_longlong_t)hdl);
3005 		(*svc->ops.ds_data_cb)(svc->ops.cb_arg, buf, buflen);
3006 	}
3007 }
3008 
3009 static int
3010 ds_loopback_set_svc(ds_svc_t *svc, ds_capability_t *cap, ds_svc_hdl_t *lb_hdlp)
3011 {
3012 	ds_svc_t *lb_svc;
3013 	ds_svc_hdl_t lb_hdl = *lb_hdlp;
3014 	int i;
3015 	int match = 0;
3016 	uint16_t new_major;
3017 	uint16_t new_minor;
3018 
3019 	if ((lb_svc = ds_get_svc(lb_hdl)) == NULL) {
3020 		DS_DBG_LOOP(CE_NOTE, "%s: loopback: hdl: 0x%llx invalid" DS_EOL,
3021 		    __func__, (u_longlong_t)lb_hdl);
3022 		return (ENXIO);
3023 	}
3024 
3025 	/* negotiate a version between loopback services, if possible */
3026 	for (i = 0; i < lb_svc->cap.nvers && match == 0; i++) {
3027 		match = negotiate_version(cap->nvers, cap->vers,
3028 		    lb_svc->cap.vers[i].major, &new_major, &new_minor);
3029 	}
3030 	if (!match) {
3031 		DS_DBG_LOOP(CE_NOTE, "%s: loopback version negotiate failed"
3032 		    DS_EOL, __func__);
3033 		return (ENOTSUP);
3034 	}
3035 	if (lb_svc->state != DS_SVC_INACTIVE) {
3036 		DS_DBG_LOOP(CE_NOTE, "%s: loopback active: hdl: 0x%llx"
3037 		    DS_EOL, __func__, (u_longlong_t)lb_hdl);
3038 		if ((lb_svc->flags & DSSF_ISCLIENT) == 0) {
3039 			DS_DBG_LOOP(CE_NOTE, "%s: loopback busy hdl: 0x%llx"
3040 			    DS_EOL, __func__, (u_longlong_t)lb_hdl);
3041 			return (EBUSY);
3042 		}
3043 		svc->state = DS_SVC_INACTIVE;	/* prevent alloc'ing svc */
3044 		lb_svc = ds_svc_clone(lb_svc);
3045 		DS_DBG_LOOP(CE_NOTE, "%s: loopback clone: ohdl: 0x%llx "
3046 		    "nhdl: 0x%llx" DS_EOL, __func__, (u_longlong_t)lb_hdl,
3047 		    (u_longlong_t)lb_svc->hdl);
3048 		*lb_hdlp = lb_svc->hdl;
3049 	}
3050 
3051 	svc->flags |= DSSF_LOOPBACK;
3052 	svc->svc_hdl = lb_svc->hdl;
3053 	svc->port = NULL;
3054 	svc->ver.major = new_major;
3055 	svc->ver.minor = new_minor;
3056 
3057 	lb_svc->flags |= DSSF_LOOPBACK;
3058 	lb_svc->svc_hdl = svc->hdl;
3059 	lb_svc->port = NULL;
3060 	lb_svc->ver.major = new_major;
3061 	lb_svc->ver.minor = new_minor;
3062 
3063 	DS_DBG_LOOP(CE_NOTE, "%s: setting loopback between: 0x%llx and 0x%llx"
3064 	    DS_EOL, __func__, (u_longlong_t)svc->hdl,
3065 	    (u_longlong_t)lb_svc->hdl);
3066 	return (0);
3067 }
3068 
3069 static ds_svc_t *
3070 ds_find_clnt_svc_by_hdl_port(ds_svc_hdl_t hdl, ds_port_t *port)
3071 {
3072 	int		idx;
3073 	ds_svc_t	*svc;
3074 
3075 	DS_DBG_PRCL(CE_NOTE, "ds@%lx: %s looking up clnt hdl: 0x%llx" DS_EOL,
3076 	    PORTID(port), __func__, (u_longlong_t)hdl);
3077 	ASSERT(MUTEX_HELD(&ds_svcs.lock));
3078 
3079 	/* walk every table entry */
3080 	for (idx = 0; idx < ds_svcs.maxsvcs; idx++) {
3081 		svc = ds_svcs.tbl[idx];
3082 		if (DS_SVC_ISFREE(svc))
3083 			continue;
3084 		if ((svc->flags & DSSF_ISCLIENT) != 0 &&
3085 		    svc->svc_hdl == hdl && svc->port == port) {
3086 			DS_DBG_PRCL(CE_NOTE, "ds@%lx: %s found clnt hdl "
3087 			    "0x%llx: svc%d" DS_EOL, PORTID(port), __func__,
3088 			    (u_longlong_t)hdl, (uint_t)DS_HDL2IDX(svc->hdl));
3089 			return (svc);
3090 		}
3091 	}
3092 	DS_DBG_PRCL(CE_NOTE, "ds@%lx: %s clnt hdl: 0x%llx not found" DS_EOL,
3093 	    PORTID(port), __func__, (u_longlong_t)hdl);
3094 
3095 	return (NULL);
3096 }
3097 
3098 static ds_svc_t *
3099 ds_svc_clone(ds_svc_t *svc)
3100 {
3101 	ds_svc_t *newsvc;
3102 	ds_svc_hdl_t hdl;
3103 
3104 	ASSERT(svc->flags & DSSF_ISCLIENT);
3105 
3106 	newsvc = ds_alloc_svc();
3107 
3108 	/* Can only clone clients for now */
3109 	hdl = newsvc->hdl | DS_HDL_ISCLIENT_BIT;
3110 	DS_DBG_USR(CE_NOTE, "%s: cloning client: old hdl: 0x%llx new hdl: "
3111 	    "0x%llx" DS_EOL, __func__, (u_longlong_t)svc->hdl,
3112 	    (u_longlong_t)hdl);
3113 	(void) memcpy(newsvc, svc, sizeof (ds_svc_t));
3114 	newsvc->hdl = hdl;
3115 	newsvc->flags &= ~DSSF_LOOPBACK;
3116 	newsvc->port = NULL;
3117 	newsvc->svc_hdl = DS_BADHDL2;
3118 	newsvc->cap.svc_id = ds_strdup(svc->cap.svc_id);
3119 	newsvc->cap.vers = DS_MALLOC(svc->cap.nvers * sizeof (ds_ver_t));
3120 	(void) memcpy(newsvc->cap.vers, svc->cap.vers,
3121 	    svc->cap.nvers * sizeof (ds_ver_t));
3122 
3123 	/*
3124 	 * Kludge to allow lds driver user callbacks to get access to current
3125 	 * svc structure.  Arg could be index to svc table or some other piece
3126 	 * of info to get to the svc table entry.
3127 	 */
3128 	if (newsvc->flags & DSSF_ISUSER) {
3129 		newsvc->ops.cb_arg = (ds_cb_arg_t)(newsvc);
3130 	}
3131 	return (newsvc);
3132 }
3133 
3134 /*
3135  * Internal handle lookup function.
3136  */
3137 static int
3138 i_ds_hdl_lookup(char *service, uint_t is_client, ds_svc_hdl_t *hdlp,
3139     uint_t maxhdls)
3140 {
3141 	int idx;
3142 	int nhdls = 0;
3143 	ds_svc_t *svc;
3144 	uint32_t client_flag = is_client ? DSSF_ISCLIENT : 0;
3145 
3146 	ASSERT(MUTEX_HELD(&ds_svcs.lock));
3147 
3148 	for (idx = 0; idx < ds_svcs.maxsvcs && nhdls < maxhdls; idx++) {
3149 		svc = ds_svcs.tbl[idx];
3150 		if (DS_SVC_ISFREE(svc))
3151 			continue;
3152 		if (strcmp(svc->cap.svc_id, service) == 0 &&
3153 		    (svc->flags & DSSF_ISCLIENT) == client_flag) {
3154 			if (hdlp != NULL && nhdls < maxhdls) {
3155 				hdlp[nhdls] = svc->hdl;
3156 				nhdls++;
3157 			} else {
3158 				nhdls++;
3159 			}
3160 		}
3161 	}
3162 	return (nhdls);
3163 }
3164 
3165 /*
3166  * Interface for ds_hdl_lookup in lds driver.
3167  */
3168 int
3169 ds_hdl_lookup(char *service, uint_t is_client, ds_svc_hdl_t *hdlp,
3170     uint_t maxhdls, uint_t *nhdlsp)
3171 {
3172 	mutex_enter(&ds_svcs.lock);
3173 	*nhdlsp = i_ds_hdl_lookup(service, is_client, hdlp, maxhdls);
3174 	mutex_exit(&ds_svcs.lock);
3175 	return (0);
3176 }
3177 
3178 static void
3179 ds_portset_del_active_clients(char *service, ds_portset_t *portsp)
3180 {
3181 	ds_portset_t ports;
3182 	int idx;
3183 	ds_svc_t *svc;
3184 
3185 	ASSERT(MUTEX_HELD(&ds_svcs.lock));
3186 
3187 	DS_PORTSET_DUP(ports, *portsp);
3188 	for (idx = 0; idx < ds_svcs.maxsvcs; idx++) {
3189 		svc = ds_svcs.tbl[idx];
3190 		if (DS_SVC_ISFREE(svc))
3191 			continue;
3192 		if (strcmp(svc->cap.svc_id, service) == 0 &&
3193 		    (svc->flags & DSSF_ISCLIENT) != 0 &&
3194 		    svc->state != DS_SVC_INACTIVE &&
3195 		    svc->port != NULL) {
3196 			DS_PORTSET_DEL(ports, PORTID(svc->port));
3197 		}
3198 	}
3199 
3200 	/*
3201 	 * Never send a client reg req to the SP.
3202 	 */
3203 	if (ds_sp_port_id != DS_PORTID_INVALID) {
3204 		DS_PORTSET_DEL(ports, ds_sp_port_id);
3205 	}
3206 	DS_PORTSET_DUP(*portsp, ports);
3207 }
3208 
3209 /*
3210  * After an UNREG REQ, check if this is a client service with multiple
3211  * handles.  If it is, then we can eliminate this entry.
3212  */
3213 static void
3214 ds_check_for_dup_services(ds_svc_t *svc)
3215 {
3216 	if ((svc->flags & DSSF_ISCLIENT) != 0 &&
3217 	    svc->state == DS_SVC_INACTIVE &&
3218 	    i_ds_hdl_lookup(svc->cap.svc_id, 1, NULL, 2) == 2) {
3219 		ds_delete_svc_entry(svc);
3220 	}
3221 }
3222 
3223 static void
3224 ds_delete_svc_entry(ds_svc_t *svc)
3225 {
3226 	ds_svc_hdl_t tmp_hdl;
3227 
3228 	ASSERT(MUTEX_HELD(&ds_svcs.lock));
3229 
3230 	/*
3231 	 * Clear out the structure, but do not deallocate the
3232 	 * memory. It can be reused for the next registration.
3233 	 */
3234 	DS_FREE(svc->cap.svc_id, strlen(svc->cap.svc_id) + 1);
3235 	DS_FREE(svc->cap.vers, svc->cap.nvers * sizeof (ds_ver_t));
3236 
3237 	/* save the handle to prevent reuse */
3238 	tmp_hdl = svc->hdl;
3239 	bzero((void *)svc, sizeof (ds_svc_t));
3240 
3241 	/* initialize for next use */
3242 	svc->hdl = tmp_hdl;
3243 	svc->state = DS_SVC_FREE;
3244 
3245 	ds_svcs.nsvcs--;
3246 }
3247