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