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]
23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 #include <sys/fm/protocol.h>
28 #include <sys/bitmap.h>
34 #include <fmd_alloc.h>
35 #include <fmd_string.h>
36 #include <fmd_module.h>
37 #include <fmd_dispq.h>
42 static fmd_dispqelem_t
*
43 fmd_dispqelem_create(const char *name
)
45 fmd_dispqelem_t
*dep
= fmd_alloc(sizeof (fmd_dispqelem_t
), FMD_SLEEP
);
47 dep
->dq_name
= fmd_strdup(name
, FMD_SLEEP
);
49 dep
->dq_hashlen
= fmd
.d_str_buckets
;
50 dep
->dq_hash
= fmd_zalloc(sizeof (void *) * dep
->dq_hashlen
, FMD_SLEEP
);
58 fmd_dispqelem_destroy(fmd_dispqelem_t
*dep
)
60 fmd_dispqlist_t
*dlp
, *nlp
;
61 fmd_dispqelem_t
*p
, *q
;
64 for (dlp
= dep
->dq_list
; dlp
!= NULL
; dlp
= nlp
) {
66 fmd_free(dlp
, sizeof (fmd_dispqlist_t
));
69 for (i
= 0; i
< dep
->dq_hashlen
; i
++) {
70 for (p
= dep
->dq_hash
[i
]; p
!= NULL
; p
= q
) {
72 fmd_dispqelem_destroy(p
);
76 fmd_free(dep
->dq_hash
, sizeof (void *) * dep
->dq_hashlen
);
77 fmd_strfree(dep
->dq_name
);
79 fmd_free(dep
, sizeof (fmd_dispqelem_t
));
82 static fmd_dispqelem_t
*
83 fmd_dispqelem_lookup(fmd_dispqelem_t
*dep
, const char *name
)
85 uint_t h
= fmd_strhash(name
) % dep
->dq_hashlen
;
87 for (dep
= dep
->dq_hash
[h
]; dep
!= NULL
; dep
= dep
->dq_link
) {
88 if (strcmp(dep
->dq_name
, name
) == 0)
96 fmd_dispq_create(void)
98 fmd_dispq_t
*dqp
= fmd_alloc(sizeof (fmd_dispq_t
), FMD_SLEEP
);
100 (void) pthread_rwlock_init(&dqp
->dq_lock
, NULL
);
101 dqp
->dq_root
= fmd_dispqelem_create(NULL
);
102 dqp
->dq_gids
= fmd_idspace_create("dispq_gids", 1, INT_MAX
);
109 fmd_dispq_destroy(fmd_dispq_t
*dqp
)
111 fmd_dispqelem_destroy(dqp
->dq_root
);
112 fmd_idspace_destroy(dqp
->dq_gids
);
113 fmd_free(dqp
, sizeof (fmd_dispq_t
));
116 static fmd_dispqelem_t
*
117 fmd_dispq_insert_one(fmd_dispqelem_t
*dep
, const char *name
)
119 uint_t h
= fmd_strhash(name
) % dep
->dq_hashlen
;
122 for (ep
= dep
->dq_hash
[h
]; ep
!= NULL
; ep
= ep
->dq_link
) {
123 if (strcmp(ep
->dq_name
, name
) == 0)
128 ep
= fmd_dispqelem_create(name
);
130 ep
->dq_link
= dep
->dq_hash
[h
];
131 dep
->dq_hash
[h
] = ep
;
134 ASSERT(dep
->dq_refs
!= 0);
141 fmd_dispq_insert(fmd_dispq_t
*dqp
, fmd_eventq_t
*eqp
, const char *pattern
)
143 char *p
, *q
, *s
= fmd_strdup(pattern
, FMD_SLEEP
);
144 size_t len
= strlen(s
);
146 fmd_dispqlist_t
*dlp
= fmd_alloc(sizeof (fmd_dispqlist_t
), FMD_SLEEP
);
147 fmd_dispqelem_t
*dep
;
149 (void) pthread_rwlock_wrlock(&dqp
->dq_lock
);
152 for (p
= strtok_r(s
, ".", &q
); p
!= NULL
; p
= strtok_r(NULL
, ".", &q
))
153 dep
= fmd_dispq_insert_one(dep
, p
);
155 dlp
->dq_next
= dep
->dq_list
;
156 dlp
->dq_eventq
= eqp
;
160 ASSERT(dep
->dq_refs
!= 0);
162 (void) pthread_rwlock_unlock(&dqp
->dq_lock
);
163 fmd_free(s
, len
+ 1);
167 fmd_dispq_delete_one(fmd_dispqelem_t
*dep
,
168 fmd_eventq_t
*eqp
, int patc
, char *patv
[])
170 fmd_dispqlist_t
*lp
, **lpp
;
171 fmd_dispqelem_t
*ep
, **epp
;
173 uint_t h
= fmd_strhash(patv
[0]) % dep
->dq_hashlen
;
174 epp
= &dep
->dq_hash
[h
];
176 for (ep
= *epp
; ep
!= NULL
; ep
= ep
->dq_link
) {
177 if (strcmp(ep
->dq_name
, patv
[0]) != 0)
187 fmd_dispq_delete_one(ep
, eqp
, patc
- 1, patv
+ 1);
189 for (lp
= *lpp
; lp
!= NULL
; lp
= lp
->dq_next
) {
190 if (lp
->dq_eventq
!= eqp
)
198 fmd_free(lp
, sizeof (fmd_dispqlist_t
));
199 ASSERT(ep
->dq_refs
!= 0);
204 if (ep
->dq_refs
== 0) {
206 fmd_dispqelem_destroy(ep
);
207 ASSERT(dep
->dq_refs
!= 0);
213 fmd_dispq_delete(fmd_dispq_t
*dqp
, fmd_eventq_t
*eqp
, const char *pattern
)
215 char *p
, *q
, *s
= fmd_strdup(pattern
, FMD_SLEEP
);
216 size_t len
= strlen(s
);
218 char **patv
= fmd_zalloc(sizeof (char *) * (len
/ 2 + 1), FMD_SLEEP
);
221 for (p
= strtok_r(s
, ".", &q
); p
!= NULL
; p
= strtok_r(NULL
, ".", &q
))
225 (void) pthread_rwlock_wrlock(&dqp
->dq_lock
);
226 fmd_dispq_delete_one(dqp
->dq_root
, eqp
, patc
, patv
);
227 (void) pthread_rwlock_unlock(&dqp
->dq_lock
);
230 fmd_free(patv
, sizeof (char *) * (len
/ 2 + 1));
231 fmd_free(s
, len
+ 1);
235 fmd_dispq_dispatch_one(fmd_dispqelem_t
*dep
, ulong_t
*gids
,
236 fmd_event_t
*ep
, const char *class)
238 fmd_dispqlist_t
*dlp
;
241 for (dlp
= dep
->dq_list
; dlp
!= NULL
; dlp
= dlp
->dq_next
, n
++) {
242 id_t gid
= dlp
->dq_eventq
->eq_sgid
;
244 if (BT_TEST(gids
, gid
) != 0)
245 continue; /* event already queued for this group ID */
247 TRACE((FMD_DBG_DISP
, "queue %p (%s) for %s (%d)", (void *)ep
,
248 class, dlp
->dq_eventq
->eq_mod
->mod_name
, (int)gid
));
250 fmd_eventq_insert_at_time(dlp
->dq_eventq
, ep
);
258 * This function handles the descent of the dispatch queue hash tree on behalf
259 * of fmd_dispq_dispatch(). We recursively descend the tree along two paths:
260 * one using the next component of the split class string (stored in cv[0]) and
261 * one using the wildcard "*" in place of cv[0]. If we can't find either one,
262 * our descent stops. If we descend far enough to consume cv[] (i.e. cc == 0),
263 * then we have a match and we dispatch the event to all modules at that level.
264 * We also dispatch the event to modules found at any interior "*" element,
265 * allowing a subscription to "a.*" to match "a.b", "a.b.c", and so on.
268 fmd_dispq_dispatchv(fmd_dispqelem_t
*root
, ulong_t
*gids
,
269 fmd_event_t
*ep
, const char *class, uint_t cc
, char *cv
[])
271 fmd_dispqelem_t
*dep
;
275 return (fmd_dispq_dispatch_one(root
, gids
, ep
, class));
277 if ((dep
= fmd_dispqelem_lookup(root
, cv
[0])) != NULL
)
278 n
+= fmd_dispq_dispatchv(dep
, gids
, ep
, class, cc
- 1, cv
+ 1);
280 if ((dep
= fmd_dispqelem_lookup(root
, "*")) != NULL
)
281 n
+= fmd_dispq_dispatchv(dep
, gids
, ep
, class, cc
- 1, cv
+ 1);
283 if (dep
!= NULL
&& cc
> 1)
284 n
+= fmd_dispq_dispatch_one(dep
, gids
, ep
, class);
290 fmd_dispq_tokenize(const char *class,
291 char *buf
, size_t buflen
, char **cv
, uint_t cvlen
)
296 (void) strlcpy(buf
, class, buflen
);
298 for (p
= strtok_r(buf
, ".", &q
); p
!= NULL
; p
= strtok_r(NULL
, ".", &q
))
302 fmd_panic("fmd_dispq_tokenize() cc=%u > cv[%u]\n", cc
, cvlen
);
308 fmd_dispq_dispatch_gid(fmd_dispq_t
*dqp
,
309 fmd_event_t
*ep
, const char *class, id_t gid
)
311 size_t cvbuflen
= strlen(class) + 1;
312 uint_t cc
, cvlen
, n
= 0;
313 char *c
, *cvbuf
, **cv
;
324 * If the event is a protocol list.suspect event with one or more
325 * events contained inside of it, determine the maximum length of all
326 * class strings that will be used in this dispatch operation.
328 if (FMD_EVENT_TYPE(ep
) == FMD_EVT_PROTOCOL
&&
329 (strcmp(class, FM_LIST_SUSPECT_CLASS
) == 0 ||
330 strcmp(class, FM_LIST_REPAIRED_CLASS
) == 0 ||
331 strcmp(class, FM_LIST_RESOLVED_CLASS
) == 0 ||
332 strcmp(class, FM_LIST_UPDATED_CLASS
) == 0) &&
333 nvlist_lookup_nvlist_array(FMD_EVENT_NVL(ep
), FM_SUSPECT_FAULT_LIST
,
335 for (nvi
= 0; nvi
< nvc
; nvi
++) {
336 if (nvlist_lookup_string(nva
[nvi
], FM_CLASS
, &c
) == 0) {
337 size_t len
= strlen(c
) + 1;
338 cvbuflen
= MAX(cvbuflen
, len
);
343 cvbuf
= alloca(cvbuflen
);
344 cvlen
= cvbuflen
/ 2 + 1;
345 cv
= alloca(sizeof (char *) * cvlen
);
348 * With dq_lock held as reader, allocate a bitmap on the stack for
349 * group IDs for this dispatch, zero it, and then do the dispatch.
351 (void) pthread_rwlock_rdlock(&dqp
->dq_lock
);
353 glen
= BT_BITOUL(dqp
->dq_gmax
);
354 gids
= alloca(sizeof (ulong_t
) * glen
);
355 bzero(gids
, sizeof (ulong_t
) * glen
);
358 * If we are dispatching to only a single gid, set all bits in the
359 * group IDs mask and then clear only the bit for the specified gid.
362 for (i
= 0; i
< glen
; i
++)
363 gids
[i
] = BT_ULMAXMASK
;
367 for (nvi
= 0; nvi
< nvc
; nvi
++) {
368 if (nvlist_lookup_string(nva
[nvi
], FM_CLASS
, &c
) == 0) {
369 cc
= fmd_dispq_tokenize(c
, cvbuf
, cvbuflen
, cv
, cvlen
);
370 n
+= fmd_dispq_dispatchv(dqp
->dq_root
,
371 gids
, ep
, c
, cc
, cv
);
375 cc
= fmd_dispq_tokenize(class, cvbuf
, cvbuflen
, cv
, cvlen
);
376 n
+= fmd_dispq_dispatchv(dqp
->dq_root
, gids
, ep
, class, cc
, cv
);
378 (void) pthread_rwlock_unlock(&dqp
->dq_lock
);
379 fmd_dprintf(FMD_DBG_DISP
, "%s dispatched to %u queues\n", class, n
);
382 * If the total subscriptions matched (n) was zero and we're not being
383 * called for a single gid, send the event to the self-diagnosis module.
385 if (n
== 0 && gid
< 0 && fmd
.d_self
!= NULL
)
386 fmd_eventq_insert_at_time(fmd
.d_self
->mod_queue
, ep
);
392 fmd_dispq_dispatch(fmd_dispq_t
*dqp
, fmd_event_t
*ep
, const char *class)
394 fmd_dispq_dispatch_gid(dqp
, ep
, class, -1);
398 fmd_dispq_getgid(fmd_dispq_t
*dqp
, void *cookie
)
402 (void) pthread_rwlock_wrlock(&dqp
->dq_lock
);
404 gid
= fmd_idspace_alloc_min(dqp
->dq_gids
, cookie
);
405 dqp
->dq_gmax
= MAX(dqp
->dq_gmax
, gid
);
407 (void) pthread_rwlock_unlock(&dqp
->dq_lock
);
413 fmd_dispq_delgid(fmd_dispq_t
*dqp
, id_t gid
)
415 (void) pthread_rwlock_wrlock(&dqp
->dq_lock
);
417 ASSERT(fmd_idspace_contains(dqp
->dq_gids
, gid
));
418 (void) fmd_idspace_free(dqp
->dq_gids
, gid
);
420 (void) pthread_rwlock_unlock(&dqp
->dq_lock
);