4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 #pragma ident "%Z%%M% %I% %E% SMI"
40 #include <librcm_impl.h>
42 #include "librcm_event.h"
44 #define dprint if (debug) (void) printf
47 #define BUF_THRESHOLD 1024 /* larger bufs require a free */
50 * Lookup seq_num. We can not use the standard nvlist_lookup functions since
51 * the nvlist is not allocated with NV_UNIQUE_NAME or NV_UNIQUE_NAME_TYPE.
54 lookup_seq_num(nvlist_t
*nvl
, uint64_t *seq_num
)
58 while ((nvp
= nvlist_next_nvpair(nvl
, nvp
)) != NULL
) {
59 if (strcmp(nvpair_name(nvp
), RCM_SEQ_NUM
) == 0 &&
60 nvpair_type(nvp
) == DATA_TYPE_UINT64
)
61 return (nvpair_value_uint64(nvp
, seq_num
));
68 * Get event service from a named door.
70 * This is similar to sysevent_post_event(), except that it deals with
71 * the "return buffer problem":
72 * Typically, the door service places the return buffer on the stack
73 * when calling door_return(). This places an artificial limit on the
74 * size of the return buffer.
75 * This problem is solved by placing large buffers on the heap, referenced
76 * through door_info. When client detects a large buffer, it will make a
77 * second door_call() to free the buffer. The client and the server agrees
78 * on a size, which is defined as BUF_THRESHOLD.
80 * Returns -1 if message not delivered. With errno set to cause of error.
81 * Returns 0 for success with the results returned in posting buffer.
84 get_event_service(char *door_name
, void *data
, size_t datalen
,
85 void **result
, size_t *rlen
)
87 int service_door
, error
;
91 * Open the service door
93 if ((service_door
= open(door_name
, O_RDONLY
, 0)) == -1) {
99 door_arg
.rbuf
= NULL
; /* doorfs will provide return buf */
101 door_arg
.data_ptr
= data
;
102 door_arg
.data_size
= datalen
;
103 door_arg
.desc_ptr
= NULL
;
104 door_arg
.desc_num
= 0;
108 * EAGAIN is returned when the door server is temporarily
109 * out of threads to service the door call. So retry.
111 if ((error
= door_call(service_door
, &door_arg
)) == -1 &&
117 if ((error
== 0) && result
) {
119 uint64_t seq_num
= 0;
123 if (door_arg
.rbuf
== NULL
|| door_arg
.rsize
== 0) {
124 dprint("bad return from door call\n");
125 (void) close(service_door
);
130 (void) nvlist_unpack(door_arg
.rbuf
, door_arg
.rsize
,
131 (nvlist_t
**)result
, 0);
132 (void) munmap(door_arg
.rbuf
, door_arg
.rsize
);
135 * If requiring a buf free, make another door call. There is
136 * no need to call munmap() after this door call, though.
138 if (lookup_seq_num((nvlist_t
*)*result
, &seq_num
) == 0) {
140 door_arg
.rbuf
= NULL
;
142 door_arg
.data_ptr
= (char *)&seq_num
;
143 door_arg
.data_size
= sizeof (seq_num
);
144 door_arg
.desc_ptr
= NULL
;
145 door_arg
.desc_num
= 0;
146 if (door_call(service_door
, &door_arg
) == -1) {
147 if (errno
== EAGAIN
) {
151 dprint("fail to free event buf in server\n");
156 (void) close(service_door
);
161 * Export an event service door
164 struct door_result
*next
;
169 typedef struct door_cookie
{
172 void (*door_func
)(void **, size_t *);
173 struct door_result
*results
;
177 * add result to cookie, this is only invoked if result size > BUF_THRESHOLD
180 add_door_result(door_cookie_t
*cook
, void *data
, uint64_t seq_num
)
182 struct door_result
*result
;
185 * Need a better way to handle memory here
187 result
= malloc(sizeof (*result
));
188 while (result
== NULL
) {
190 result
= malloc(sizeof (*result
));
194 result
->seq_num
= seq_num
;
197 * Attach current door result to the door cookie
199 (void) mutex_lock(&cook
->door_lock
);
200 if (cook
->results
== NULL
) {
201 cook
->results
= result
;
203 struct door_result
*tmp
= cook
->results
;
209 (void) mutex_unlock(&cook
->door_lock
);
213 * free a previous door result as described by number.
216 free_door_result(door_cookie_t
*cook
, uint64_t num
)
218 struct door_result
*prev
= NULL
, *tmp
;
220 (void) mutex_lock(&cook
->door_lock
);
222 while (tmp
&& tmp
->seq_num
!= num
) {
228 dprint("attempting to free nonexistent buf: %llu\n",
229 (unsigned long long)num
);
230 (void) mutex_unlock(&cook
->door_lock
);
235 prev
->next
= tmp
->next
;
237 cook
->results
= tmp
->next
;
239 (void) mutex_unlock(&cook
->door_lock
);
247 door_service(void *cookie
, char *args
, size_t alen
,
248 door_desc_t
*ddp
, uint_t ndid
)
252 char rbuf
[BUF_THRESHOLD
];
253 door_cookie_t
*cook
= (door_cookie_t
*)cookie
;
254 uint64_t seq_num
= 0;
257 * Special case for asking to free buffer
259 if (alen
== sizeof (uint64_t)) {
260 free_door_result(cookie
, *(uint64_t *)(void *)args
);
261 (void) door_return(NULL
, 0, NULL
, 0);
265 * door_func update args to point to return results.
266 * memory for results are dynamically allocated.
268 (*cook
->door_func
)((void **)&args
, &alen
);
271 * If no results, just return
274 dprint("null results returned from door_func().\n");
275 (void) door_return(NULL
, 0, NULL
, 0);
278 /* Determine the size of the packed nvlist */
279 nvl
= (nvlist_t
*)(void *)args
;
282 if (errno
= nvlist_size(nvl
, &nvl_size
, NV_ENCODE_NATIVE
)) {
284 dprint("failure to sizeup door results: %s\n", strerror(errno
));
285 (void) door_return(NULL
, 0, NULL
, 0);
289 * If the size of the packed nvlist would exceed the buffer threshold
290 * then get a sequence number and add it to the nvlist.
292 if (nvl_size
> BUF_THRESHOLD
) {
293 (void) mutex_lock(&cook
->door_lock
);
295 seq_num
= cook
->seq_num
;
296 (void) mutex_unlock(&cook
->door_lock
);
297 (void) nvlist_add_uint64(nvl
, RCM_SEQ_NUM
, seq_num
);
300 /* Refill the args with a packed version of the nvlist */
301 if (errno
= nvlist_pack(nvl
, &args
, &alen
, NV_ENCODE_NATIVE
, 0)) {
303 dprint("failure to pack door results: %s\n", strerror(errno
));
304 (void) door_return(NULL
, 0, NULL
, 0);
309 * Based on the size of the packed nvlist, either use the local buffer
310 * or add it to the results list.
312 if (alen
<= BUF_THRESHOLD
) {
313 bcopy(args
, rbuf
, alen
);
318 * for long data, append results to end of queue in cook
319 * and set ndid, ask client to do another door_call
320 * to free the buffer.
322 add_door_result(cook
, args
, seq_num
);
325 (void) door_return(args
, alen
, NULL
, 0);
329 create_event_service(char *door_name
,
330 void (*func
)(void **data
, size_t *datalen
))
332 int service_door
, fd
;
333 door_cookie_t
*cookie
;
335 /* create an fs file */
336 fd
= open(door_name
, O_EXCL
|O_CREAT
, S_IREAD
|S_IWRITE
);
337 if ((fd
== -1) && (errno
!= EEXIST
)) {
342 /* allocate space for door cookie */
343 if ((cookie
= calloc(1, sizeof (*cookie
))) == NULL
) {
347 cookie
->door_func
= func
;
348 if ((service_door
= door_create(door_service
, (void *)cookie
,
349 DOOR_REFUSE_DESC
| DOOR_NO_CANCEL
)) == -1) {
350 dprint("door create failed: %s\n", strerror(errno
));
356 (void) fdetach(door_name
);
357 if (fattach(service_door
, door_name
) != 0) {
358 if (errno
== EBUSY
) {
360 * EBUSY error may occur if anyone references the door
361 * file while we are fattach'ing. Since librcm, in the
362 * the process context of a DR initiator program, may
363 * reference the door file (via open/close/stat/
364 * door_call etc.) while we are still fattach'ing,
369 dprint("door attaching failed: %s\n", strerror(errno
));
371 (void) close(service_door
);
375 return (service_door
);
379 revoke_event_service(int fd
)
381 struct door_info info
;
382 door_cookie_t
*cookie
;
384 if (door_info(fd
, &info
) == -1) {
388 if (door_revoke(fd
) != 0) {
392 /* wait for existing door calls to finish */
395 if ((cookie
= (door_cookie_t
*)(uintptr_t)info
.di_data
) != NULL
) {
396 struct door_result
*tmp
= cookie
->results
;
398 cookie
->results
= tmp
->next
;
401 tmp
= cookie
->results
;