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.
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.
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]
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
32 #include <sys/types.h>
33 #include <sys/sysevent.h>
34 #include <libsysevent.h>
38 #define PTRTOUINT64(ptr) ((uint64_t)((uintptr_t)(ptr)))
39 static char vlds_device
[] =
40 "/devices/virtual-devices@100/channel-devices@200/"
41 "virtual-domain-service@0:vlds";
43 typedef struct dslibentry
{
52 #define DSL_ENTRY_INUSE 0x0001 /* handle is currently active */
54 #define MIN_DSLIB_ENTRIES 64
55 static dslibentry_t
*dslibtab
;
59 * Lock to protect the dslibtab table. We only need to protect this
60 * table for those functions which actually look at or modify the table:
61 * service registration (ds_svc_reg/ds_clnt_reg), service unregistration
62 * (ds_hdl_unreg) or during callbacks (ds_recv)
64 static mutex_t dslib_lock
;
66 static int ds_fd
= -1;
68 static char *ds_sid_name
= "vlds";
70 static evchan_t
*ds_evchan
;
73 * Static functions internal to dslib.
75 static dslibentry_t
*ds_hdl_to_dslibentry(ds_hdl_t hdl
);
76 static dslibentry_t
*ds_new_dslibentry(void);
77 static uint_t
ds_service_count(char *service
, boolean_t is_client
);
78 static dslibentry_t
*ds_lookup_dslibentry(char *service
, boolean_t is_client
);
79 static dslibentry_t
*ds_register_dslibentry(ds_hdl_t hdl
, char *service
,
81 static void ds_free_dslibentry(dslibentry_t
*dsp
, int force_unreg
);
82 static int ds_recv(sysevent_t
*sep
, void *arg
);
83 static void ds_string_arg(vlds_string_t
*dsp
, char *str
);
84 static int ds_register(ds_capability_t
*cap
, ds_ops_t
*ops
, uint_t flags
);
87 ds_hdl_to_dslibentry(ds_hdl_t hdl
)
92 for (i
= 0, dsp
= dslibtab
; i
< ndslib
; i
++, dsp
++) {
93 if (hdl
== dsp
->dsl_hdl
)
100 ds_new_dslibentry(void)
105 if ((dsp
= ds_hdl_to_dslibentry(NULL
)) != NULL
)
108 /* double the size */
109 newndslib
= ndslib
<< 1;
110 if ((dslibtab
= realloc(dslibtab
, newndslib
* sizeof (dslibentry_t
)))
113 dsp
= &dslibtab
[ndslib
];
114 (void) memset(dsp
, 0, (newndslib
- ndslib
) * sizeof (dslibentry_t
));
120 ds_service_count(char *service
, boolean_t is_client
)
124 uint_t is_client_flag
= is_client
? VLDS_REG_CLIENT
: 0;
127 for (i
= 0, dsp
= dslibtab
; i
< ndslib
; i
++, dsp
++) {
128 if (dsp
->dsl_hdl
!= NULL
&&
129 strcmp(dsp
->dsl_service
, service
) == 0 &&
130 (dsp
->dsl_flags
& VLDS_REG_CLIENT
) == is_client_flag
) {
137 static dslibentry_t
*
138 ds_lookup_dslibentry(char *service
, boolean_t is_client
)
142 uint_t is_client_flag
= is_client
? VLDS_REG_CLIENT
: 0;
144 for (i
= 0, dsp
= dslibtab
; i
< ndslib
; i
++, dsp
++) {
145 if (dsp
->dsl_hdl
!= NULL
&&
146 strcmp(dsp
->dsl_service
, service
) == 0 &&
147 (dsp
->dsl_flags
& VLDS_REG_CLIENT
) == is_client_flag
) {
154 static dslibentry_t
*
155 ds_register_dslibentry(ds_hdl_t hdl
, char *service
, boolean_t is_client
)
157 dslibentry_t
*dsp
, *orig_dsp
;
159 if ((dsp
= ds_hdl_to_dslibentry(hdl
)) != NULL
) {
160 dsp
->dsl_tflags
|= DSL_ENTRY_INUSE
;
164 if ((orig_dsp
= ds_lookup_dslibentry(service
, is_client
)) == NULL
) {
168 if ((orig_dsp
->dsl_tflags
& DSL_ENTRY_INUSE
) == 0) {
169 /* use the original structure entry */
170 orig_dsp
->dsl_tflags
|= DSL_ENTRY_INUSE
;
171 orig_dsp
->dsl_hdl
= hdl
;
175 /* allocate a new structure entry */
176 if ((dsp
= ds_new_dslibentry()) == NULL
)
180 dsp
->dsl_service
= strdup(orig_dsp
->dsl_service
);
186 * Want to leave an entry in the dslib table even though all the
187 * handles may have been unregistered for it.
190 ds_free_dslibentry(dslibentry_t
*dsp
, int force_unreg
)
195 * Find out if we have 1 or 2 or more handles for the given
196 * service. Having one implies that we want to leave the entry
197 * intact but marked as not in use unless this is a ds_unreg_hdl
198 * (force_unreg is true).
200 nhdls
= ds_service_count(dsp
->dsl_service
,
201 (dsp
->dsl_flags
& VLDS_REG_CLIENT
) != 0);
203 if ((nhdls
== 1 && force_unreg
) || nhdls
>= 2) {
205 if (dsp
->dsl_service
) {
206 free(dsp
->dsl_service
);
208 (void) memset(dsp
, 0, sizeof (dslibentry_t
));
209 } else if (nhdls
== 1) {
210 dsp
->dsl_tflags
&= ~DSL_ENTRY_INUSE
;
216 ds_recv(sysevent_t
*sep
, void *arg
)
221 ds_domain_hdl_t dhdl
;
230 subclass
= sysevent_get_subclass_name(sep
);
231 if (sysevent_get_attr_list(sep
, &nvl
) != 0) {
235 if (nvlist_lookup_uint64(nvl
, VLDS_HDL
, &hdl
) == 0) {
236 if (strcmp(subclass
, ESC_VLDS_REGISTER
) == 0) {
237 void (*reg_cb
)(ds_hdl_t
, ds_cb_arg_t
, ds_ver_t
*,
238 ds_domain_hdl_t
) = NULL
;
240 if (nvlist_lookup_string(nvl
, VLDS_SERVICE_ID
,
242 nvlist_lookup_boolean_value(nvl
, VLDS_ISCLIENT
,
244 (void) mutex_lock(&dslib_lock
);
245 if ((dsp
= ds_register_dslibentry(hdl
,
246 servicep
, is_client
)) != NULL
) {
247 reg_cb
= dsp
->dsl_ops
.ds_reg_cb
;
248 cb_arg
= dsp
->dsl_ops
.cb_arg
;
250 (void) mutex_unlock(&dslib_lock
);
251 if (reg_cb
!= NULL
&&
252 nvlist_lookup_uint64(nvl
, VLDS_DOMAIN_HDL
,
254 nvlist_lookup_uint16(nvl
, VLDS_VER_MAJOR
,
256 nvlist_lookup_uint16(nvl
, VLDS_VER_MINOR
,
258 (reg_cb
)((ds_hdl_t
)hdl
, cb_arg
, &ver
,
262 } else if (strcmp(subclass
, ESC_VLDS_UNREGISTER
) == 0) {
263 void (*unreg_cb
)(ds_hdl_t
, ds_cb_arg_t
) = NULL
;
265 (void) mutex_lock(&dslib_lock
);
266 if ((dsp
= ds_hdl_to_dslibentry(hdl
)) != NULL
) {
267 unreg_cb
= dsp
->dsl_ops
.ds_unreg_cb
;
268 cb_arg
= dsp
->dsl_ops
.cb_arg
;
269 ds_free_dslibentry(dsp
, 0);
271 (void) mutex_unlock(&dslib_lock
);
272 if (unreg_cb
!= NULL
) {
273 (unreg_cb
)((ds_hdl_t
)hdl
, cb_arg
);
275 } else if (strcmp(subclass
, ESC_VLDS_DATA
) == 0) {
276 void (*data_cb
)(ds_hdl_t
, ds_cb_arg_t
, void *,
279 (void) mutex_lock(&dslib_lock
);
280 if ((dsp
= ds_hdl_to_dslibentry(hdl
)) != NULL
) {
281 data_cb
= dsp
->dsl_ops
.ds_data_cb
;
282 cb_arg
= dsp
->dsl_ops
.cb_arg
;
284 (void) mutex_unlock(&dslib_lock
);
285 if (data_cb
!= NULL
&&
286 nvlist_lookup_byte_array(nvl
, VLDS_DATA
, &bufp
,
288 (data_cb
)((ds_hdl_t
)hdl
, cb_arg
, bufp
, buflen
);
297 ds_string_arg(vlds_string_t
*dsp
, char *str
)
300 dsp
->vlds_strp
= NULL
;
301 dsp
->vlds_strlen
= 0;
303 dsp
->vlds_strp
= PTRTOUINT64(str
);
304 dsp
->vlds_strlen
= strlen(str
) + 1;
311 char evchan_name
[MAX_CHNAME_LEN
];
313 (void) sprintf(evchan_name
, VLDS_SYSEV_CHAN_FMT
, (int)getpid());
314 if (sysevent_evc_bind(evchan_name
, &ds_evchan
, 0) != 0) {
317 if (sysevent_evc_subscribe(ds_evchan
, ds_sid_name
, EC_VLDS
,
318 ds_recv
, NULL
, 0) != 0) {
319 (void) sysevent_evc_unbind(ds_evchan
);
332 if ((ds_fd
= open(vlds_device
, 0)) < 0)
335 if (dslibtab
== NULL
) {
336 dslibtab
= malloc(sizeof (dslibentry_t
) * MIN_DSLIB_ENTRIES
);
337 if (dslibtab
== NULL
)
338 return (errno
= ENOMEM
);
339 ndslib
= MIN_DSLIB_ENTRIES
;
340 (void) memset(dslibtab
, 0, sizeof (dslibentry_t
) * ndslib
);
343 (void) mutex_init(&dslib_lock
, USYNC_THREAD
, NULL
);
348 ds_register(ds_capability_t
*cap
, ds_ops_t
*ops
, uint_t flags
)
351 vlds_svc_reg_arg_t vlds_arg
;
353 vlds_ver_t vlds_vers
[VLDS_MAX_VERS
];
359 if (cap
== NULL
|| ops
== NULL
|| cap
->svc_id
== NULL
||
360 cap
->vers
== NULL
|| (flags
& (~VLDS_REG_CLIENT
)) != 0) {
361 return (errno
= EINVAL
);
364 if (cap
->nvers
> VLDS_MAX_VERS
) {
365 return (errno
= EINVAL
);
368 if (ds_fd
< 0 && (errno
= ds_init()) != 0) {
372 if (ds_hdl_lookup(cap
->svc_id
, (flags
& VLDS_REG_CLIENT
), NULL
, 1,
373 &nhdls
) == 0 && nhdls
== 1) {
374 return (errno
= EALREADY
);
377 (void) mutex_lock(&dslib_lock
);
378 if ((dsp
= ds_new_dslibentry()) == NULL
) {
379 (void) mutex_unlock(&dslib_lock
);
380 return (errno
= ENOMEM
);
383 /* Setup device driver capability structure. */
386 ds_string_arg(&vlds_cap
.vlds_service
, cap
->svc_id
);
389 for (i
= 0; i
< cap
->nvers
; i
++) {
390 vlds_vers
[i
].vlds_major
= cap
->vers
[i
].major
;
391 vlds_vers
[i
].vlds_minor
= cap
->vers
[i
].minor
;
393 vlds_cap
.vlds_versp
= PTRTOUINT64(vlds_vers
);
394 vlds_cap
.vlds_nver
= cap
->nvers
;
397 * Format args for VLDS_SVC_REG ioctl.
400 vlds_arg
.vlds_capp
= PTRTOUINT64(&vlds_cap
);
403 if (ops
->ds_reg_cb
!= NULL
)
404 flags
|= VLDS_REGCB_VALID
;
405 if (ops
->ds_unreg_cb
!= NULL
)
406 flags
|= VLDS_UNREGCB_VALID
;
407 if (ops
->ds_data_cb
!= NULL
)
408 flags
|= VLDS_DATACB_VALID
;
409 vlds_arg
.vlds_reg_flags
= flags
;
411 /* returned handle */
412 vlds_arg
.vlds_hdlp
= PTRTOUINT64(&hdl_arg
);
414 if (ioctl(ds_fd
, VLDS_SVC_REG
, &vlds_arg
) < 0) {
415 (void) mutex_unlock(&dslib_lock
);
420 * Setup user callback sysevent channel.
422 if ((flags
& VLDS_ANYCB_VALID
) != 0 && ds_evchan
== NULL
&&
423 ds_init_sysev() != 0) {
424 (void) mutex_unlock(&dslib_lock
);
425 (void) ioctl(ds_fd
, VLDS_UNREG_HDL
, &vlds_arg
);
432 * Set entry values in dslibtab.
435 dsp
->dsl_flags
= flags
;
437 dsp
->dsl_service
= strdup(cap
->svc_id
);
439 (void) mutex_unlock(&dslib_lock
);
444 * Registers a service provider. Kicks off the handshake with other
445 * domain(s) to announce servce. Callback events are as described above.
448 ds_svc_reg(ds_capability_t
*cap
, ds_ops_t
*ops
)
450 return (ds_register(cap
, ops
, 0));
454 * Registers interest in a service from a specific domain. When that
455 * service is registered, the register callback is invoked. When that
456 * service is unregistered, the unregister callback is invoked. When
457 * data is received, the receive data callback is invoked.
460 ds_clnt_reg(ds_capability_t
*cap
, ds_ops_t
*ops
)
462 return (ds_register(cap
, ops
, VLDS_REG_CLIENT
));
466 * Given a service name and type, returns the existing handle(s), if
467 * one or more exist. This could be used to poll for the connection being
468 * registered or unregistered, rather than using the register/unregister
472 ds_hdl_lookup(char *service
, boolean_t is_client
, ds_hdl_t
*hdlsp
,
473 uint_t maxhdls
, uint_t
*nhdlsp
)
475 vlds_hdl_lookup_arg_t vlds_arg
;
480 return (errno
= EBADF
);
483 if (service
== NULL
) {
484 return (errno
= EINVAL
);
487 ds_string_arg(&vlds_arg
.vlds_service
, service
);
488 vlds_arg
.vlds_isclient
= is_client
? VLDS_REG_CLIENT
: 0;
489 vlds_arg
.vlds_hdlsp
= PTRTOUINT64(hdlsp
);
490 vlds_arg
.vlds_maxhdls
= maxhdls
;
491 vlds_arg
.vlds_nhdlsp
= PTRTOUINT64(&nhdls_arg
);
493 if (ioctl(ds_fd
, VLDS_HDL_LOOKUP
, &vlds_arg
) < 0) {
502 * Given a handle, return its associated domain.
505 ds_domain_lookup(ds_hdl_t hdl
, ds_domain_hdl_t
*dhdlp
)
507 vlds_dmn_lookup_arg_t vlds_arg
;
511 return (errno
= EBADF
);
514 vlds_arg
.vlds_hdl
= hdl
;
515 vlds_arg
.vlds_dhdlp
= PTRTOUINT64(&dhdl_arg
);
517 if (ioctl(ds_fd
, VLDS_DMN_LOOKUP
, &vlds_arg
) < 0) {
529 * Unregisters either a service or an interest in that service
530 * indicated by the supplied handle.
533 ds_unreg_hdl(ds_hdl_t hdl
)
536 vlds_unreg_hdl_arg_t vlds_arg
;
538 (void) mutex_lock(&dslib_lock
);
539 if ((dsp
= ds_hdl_to_dslibentry(hdl
)) != NULL
) {
540 ds_free_dslibentry(dsp
, 1);
542 (void) mutex_unlock(&dslib_lock
);
545 vlds_arg
.vlds_hdl
= hdl
;
546 (void) ioctl(ds_fd
, VLDS_UNREG_HDL
, &vlds_arg
);
553 * Send data to the appropriate service provider or client
554 * indicated by the provided handle. The sender will block
555 * until the message has been sent. There is no guarantee
556 * that multiple calls to ds_send_msg by the same thread
557 * will result in the data showing up at the receiver in
558 * the same order as sent. If multiple messages are required,
559 * it will be up to the sender and receiver to implement a
563 ds_send_msg(ds_hdl_t hdl
, void *buf
, size_t buflen
)
565 vlds_send_msg_arg_t vlds_arg
;
568 return (errno
= EBADF
);
571 vlds_arg
.vlds_hdl
= hdl
;
572 vlds_arg
.vlds_bufp
= PTRTOUINT64(buf
);
573 vlds_arg
.vlds_buflen
= buflen
;
575 if (ioctl(ds_fd
, VLDS_SEND_MSG
, &vlds_arg
) < 0) {
583 * Receive data from the appropriate service provider or client
584 * indicated by the provided handle. The sender will block
585 * until a message has been received.
588 ds_recv_msg(ds_hdl_t hdl
, void *buf
, size_t buflen
, size_t *msglen
)
590 vlds_recv_msg_arg_t vlds_arg
;
594 return (errno
= EBADF
);
597 vlds_arg
.vlds_hdl
= hdl
;
598 vlds_arg
.vlds_bufp
= PTRTOUINT64(buf
);
599 vlds_arg
.vlds_buflen
= buflen
;
600 vlds_arg
.vlds_msglenp
= PTRTOUINT64(&msglen_arg
);
602 if (ioctl(ds_fd
, VLDS_RECV_MSG
, &vlds_arg
) < 0) {
603 if (errno
== EFBIG
&& msglen
) {
604 *msglen
= msglen_arg
;
610 *msglen
= msglen_arg
;
617 ds_isready(ds_hdl_t hdl
, boolean_t
*is_ready
)
619 vlds_hdl_isready_arg_t vlds_arg
;
620 uint64_t is_ready_arg
;
623 return (errno
= EBADF
);
626 vlds_arg
.vlds_hdl
= hdl
;
627 vlds_arg
.vlds_isreadyp
= PTRTOUINT64(&is_ready_arg
);
629 if (ioctl(ds_fd
, VLDS_HDL_ISREADY
, &vlds_arg
) < 0) {
633 *is_ready
= (is_ready_arg
!= 0);
638 * Given a domain name, return its associated domain handle.
641 ds_dom_name_to_hdl(char *domain_name
, ds_domain_hdl_t
*dhdlp
)
643 vlds_dom_nam2hdl_arg_t vlds_arg
;
647 return (errno
= EBADF
);
650 ds_string_arg(&vlds_arg
.vlds_domain_name
, domain_name
);
651 vlds_arg
.vlds_dhdlp
= PTRTOUINT64(&dhdl_arg
);
653 if (ioctl(ds_fd
, VLDS_DOM_NAM2HDL
, &vlds_arg
) < 0) {
665 * Given a domain handle, return its associated domain name.
668 ds_dom_hdl_to_name(ds_domain_hdl_t dhdl
, char *domain_name
, uint_t maxnamlen
)
670 vlds_dom_hdl2nam_arg_t vlds_arg
;
673 return (errno
= EBADF
);
676 vlds_arg
.vlds_dhdl
= dhdl
;
677 vlds_arg
.vlds_domain_name
.vlds_strp
= PTRTOUINT64(domain_name
);
678 vlds_arg
.vlds_domain_name
.vlds_strlen
= maxnamlen
;
680 if (ioctl(ds_fd
, VLDS_DOM_HDL2NAM
, &vlds_arg
) < 0) {
688 ds_unreg_svc(char *service
, boolean_t is_client
)
693 while (ds_hdl_lookup(service
, is_client
, &hdl
, 1, &nhdls
) == 0 &&
695 (void) ds_unreg_hdl(hdl
);
710 (void) sysevent_evc_unsubscribe(ds_evchan
, ds_sid_name
);
711 (void) sysevent_evc_unbind(ds_evchan
);
715 (void) mutex_lock(&dslib_lock
);
716 for (i
= 0, dsp
= dslibtab
; i
< ndslib
; i
++, dsp
++) {
717 if (dsp
->dsl_hdl
== NULL
)
719 if (dsp
->dsl_service
) {
720 free(dsp
->dsl_service
);
726 (void) mutex_unlock(&dslib_lock
);
727 (void) mutex_destroy(&dslib_lock
);