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