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