xref: /illumos-gate/usr/src/cmd/fm/fmd/common/fmd_dispq.c (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
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.
13  *
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]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <sys/fm/protocol.h>
30 #include <strings.h>
31 #include <alloca.h>
32 
33 #include <fmd_alloc.h>
34 #include <fmd_string.h>
35 #include <fmd_dispq.h>
36 #include <fmd_eventq.h>
37 #include <fmd_subr.h>
38 #include <fmd.h>
39 
40 static fmd_dispqelem_t *
41 fmd_dispqelem_create(const char *name)
42 {
43 	fmd_dispqelem_t *dep = fmd_alloc(sizeof (fmd_dispqelem_t), FMD_SLEEP);
44 
45 	dep->dq_name = fmd_strdup(name, FMD_SLEEP);
46 	dep->dq_link = NULL;
47 	dep->dq_hashlen = fmd.d_str_buckets;
48 	dep->dq_hash = fmd_zalloc(sizeof (void *) * dep->dq_hashlen, FMD_SLEEP);
49 	dep->dq_list = NULL;
50 	dep->dq_refs = 0;
51 
52 	return (dep);
53 }
54 
55 static void
56 fmd_dispqelem_destroy(fmd_dispqelem_t *dep)
57 {
58 	fmd_dispqlist_t *dlp, *nlp;
59 	fmd_dispqelem_t *p, *q;
60 	uint_t i;
61 
62 	for (dlp = dep->dq_list; dlp != NULL; dlp = nlp) {
63 		nlp = dlp->dq_next;
64 		fmd_module_rele(dlp->dq_mod);
65 		fmd_free(dlp, sizeof (fmd_dispqlist_t));
66 	}
67 
68 	for (i = 0; i < dep->dq_hashlen; i++) {
69 		for (p = dep->dq_hash[i]; p != NULL; p = q) {
70 			q = p->dq_link;
71 			fmd_dispqelem_destroy(p);
72 		}
73 	}
74 
75 	fmd_free(dep->dq_hash, sizeof (void *) * dep->dq_hashlen);
76 	fmd_strfree(dep->dq_name);
77 
78 	fmd_free(dep, sizeof (fmd_dispqelem_t));
79 }
80 
81 static fmd_dispqelem_t *
82 fmd_dispqelem_lookup(fmd_dispqelem_t *dep, const char *name)
83 {
84 	uint_t h = fmd_strhash(name) % dep->dq_hashlen;
85 
86 	for (dep = dep->dq_hash[h]; dep != NULL; dep = dep->dq_link) {
87 		if (strcmp(dep->dq_name, name) == 0)
88 			break;
89 	}
90 
91 	return (dep);
92 }
93 
94 fmd_dispq_t *
95 fmd_dispq_create(void)
96 {
97 	fmd_dispq_t *dqp = fmd_alloc(sizeof (fmd_dispq_t), FMD_SLEEP);
98 
99 	(void) pthread_rwlock_init(&dqp->dq_lock, NULL);
100 	dqp->dq_root = fmd_dispqelem_create(NULL);
101 
102 	return (dqp);
103 }
104 
105 void
106 fmd_dispq_destroy(fmd_dispq_t *dqp)
107 {
108 	fmd_dispqelem_destroy(dqp->dq_root);
109 	fmd_free(dqp, sizeof (fmd_dispq_t));
110 }
111 
112 static fmd_dispqelem_t *
113 fmd_dispq_insert_one(fmd_dispqelem_t *dep, const char *name)
114 {
115 	uint_t h = fmd_strhash(name) % dep->dq_hashlen;
116 	fmd_dispqelem_t *ep;
117 
118 	for (ep = dep->dq_hash[h]; ep != NULL; ep = ep->dq_link) {
119 		if (strcmp(ep->dq_name, name) == 0)
120 			break;
121 	}
122 
123 	if (ep == NULL) {
124 		ep = fmd_dispqelem_create(name);
125 
126 		ep->dq_link = dep->dq_hash[h];
127 		dep->dq_hash[h] = ep;
128 
129 		dep->dq_refs++;
130 		ASSERT(dep->dq_refs != 0);
131 	}
132 
133 	return (ep);
134 }
135 
136 void
137 fmd_dispq_insert(fmd_dispq_t *dqp, fmd_module_t *mp, const char *pattern)
138 {
139 	char *p, *q, *s = fmd_strdup(pattern, FMD_SLEEP);
140 	size_t len = strlen(s);
141 
142 	fmd_dispqlist_t *dlp = fmd_alloc(sizeof (fmd_dispqlist_t), FMD_SLEEP);
143 	fmd_dispqelem_t *dep;
144 
145 	fmd_module_hold(mp);
146 
147 	(void) pthread_rwlock_wrlock(&dqp->dq_lock);
148 	dep = dqp->dq_root;
149 
150 	for (p = strtok_r(s, ".", &q); p != NULL; p = strtok_r(NULL, ".", &q))
151 		dep = fmd_dispq_insert_one(dep, p);
152 
153 	dlp->dq_next = dep->dq_list;
154 	dlp->dq_mod = mp;
155 
156 	dep->dq_list = dlp;
157 	dep->dq_refs++;
158 	ASSERT(dep->dq_refs != 0);
159 
160 	(void) pthread_rwlock_unlock(&dqp->dq_lock);
161 	fmd_free(s, len + 1);
162 }
163 
164 static void
165 fmd_dispq_delete_one(fmd_dispqelem_t *dep,
166     fmd_module_t *mp, int patc, char *patv[])
167 {
168 	fmd_dispqlist_t *lp, **lpp;
169 	fmd_dispqelem_t *ep, **epp;
170 
171 	uint_t h = fmd_strhash(patv[0]) % dep->dq_hashlen;
172 	epp = &dep->dq_hash[h];
173 
174 	for (ep = *epp; ep != NULL; ep = ep->dq_link) {
175 		if (strcmp(ep->dq_name, patv[0]) != 0)
176 			epp = &ep->dq_link;
177 		else
178 			break;
179 	}
180 
181 	ASSERT(ep != NULL);
182 	lpp = &ep->dq_list;
183 
184 	if (patc > 1) {
185 		fmd_dispq_delete_one(ep, mp, patc - 1, patv + 1);
186 	} else {
187 		for (lp = *lpp; lp != NULL; lp = lp->dq_next) {
188 			if (lp->dq_mod != mp)
189 				lpp = &lp->dq_next;
190 			else
191 				break;
192 		}
193 
194 		ASSERT(lp != NULL);
195 		*lpp = lp->dq_next;
196 
197 		fmd_module_rele(lp->dq_mod);
198 		fmd_free(lp, sizeof (fmd_dispqlist_t));
199 
200 		ASSERT(ep->dq_refs != 0);
201 		ep->dq_refs--;
202 	}
203 
204 	if (ep->dq_refs == 0) {
205 		*epp = ep->dq_link;
206 		fmd_dispqelem_destroy(ep);
207 		ASSERT(dep->dq_refs != 0);
208 		dep->dq_refs--;
209 	}
210 }
211 
212 void
213 fmd_dispq_delete(fmd_dispq_t *dqp, fmd_module_t *mp, const char *pattern)
214 {
215 	char *p, *q, *s = fmd_strdup(pattern, FMD_SLEEP);
216 	size_t len = strlen(s);
217 
218 	char **patv = fmd_zalloc(sizeof (char *) * (len / 2 + 1), FMD_SLEEP);
219 	int patc = 0;
220 
221 	for (p = strtok_r(s, ".", &q); p != NULL; p = strtok_r(NULL, ".", &q))
222 		patv[patc++] = p;
223 
224 	if (patc != 0) {
225 		(void) pthread_rwlock_wrlock(&dqp->dq_lock);
226 		fmd_dispq_delete_one(dqp->dq_root, mp, patc, patv);
227 		(void) pthread_rwlock_unlock(&dqp->dq_lock);
228 	}
229 
230 	fmd_free(patv, sizeof (char *) * (len / 2 + 1));
231 	fmd_free(s, len + 1);
232 }
233 
234 static uint_t
235 fmd_dispq_dispatch_one(fmd_dispqelem_t *dep, fmd_event_t *ep, const char *class)
236 {
237 	fmd_dispqlist_t *dlp;
238 	uint_t n = 0;
239 
240 	for (dlp = dep->dq_list; dlp != NULL; dlp = dlp->dq_next, n++) {
241 		TRACE((FMD_DBG_DISP, "queue %p (%s) for %s",
242 		    (void *)ep, class, dlp->dq_mod->mod_name));
243 
244 		fmd_eventq_insert_at_time(dlp->dq_mod->mod_queue, ep);
245 	}
246 
247 	return (n);
248 }
249 
250 /*
251  * This function handles the descent of the dispatch queue hash tree on behalf
252  * of fmd_dispq_dispatch().  We recursively descend the tree along two paths:
253  * one using the next component of the split class string (stored in cv[0]) and
254  * one using the wildcard "*" in place of cv[0].  If we can't find either one,
255  * our descent stops.  If we descend far enough to consume cv[] (i.e. cc == 0),
256  * then we have a match and we dispatch the event to all modules at that level.
257  * We also dispatch the event to modules found at any interior "*" element,
258  * allowing a subscription to "a.*" to match "a.b", "a.b.c", and so on.
259  */
260 static uint_t
261 fmd_dispq_dispatchv(fmd_dispqelem_t *root,
262     fmd_event_t *ep, const char *class, uint_t cc, char *cv[])
263 {
264 	fmd_dispqelem_t *dep;
265 	uint_t n = 0;
266 
267 	if (cc == 0)
268 		return (fmd_dispq_dispatch_one(root, ep, class));
269 
270 	if ((dep = fmd_dispqelem_lookup(root, cv[0])) != NULL)
271 		n += fmd_dispq_dispatchv(dep, ep, class, cc - 1, cv + 1);
272 
273 	if ((dep = fmd_dispqelem_lookup(root, "*")) != NULL)
274 		n += fmd_dispq_dispatchv(dep, ep, class, cc - 1, cv + 1);
275 
276 	if (dep != NULL && cc > 1)
277 		n += fmd_dispq_dispatch_one(dep, ep, class);
278 
279 	return (n);
280 }
281 
282 void
283 fmd_dispq_dispatch(fmd_dispq_t *dqp, fmd_event_t *ep, const char *class)
284 {
285 	fmd_event_impl_t *eip = (fmd_event_impl_t *)ep;
286 	char *p, *q, *s, **cv;
287 	uint_t n, len, cc;
288 
289 	nvlist_t **nvp;
290 	uint_t nvc = 0;
291 
292 	fmd_event_hold(ep);
293 
294 	/*
295 	 * If the event is a protocol list.suspect event with one or more
296 	 * events contained inside of it, we call fmd_dispq_dispatch()
297 	 * recursively using the class string of the embedded event.
298 	 */
299 	if (eip->ev_type == FMD_EVT_PROTOCOL && strcmp(class,
300 	    FM_LIST_SUSPECT_CLASS) == 0 && nvlist_lookup_nvlist_array(
301 	    eip->ev_nvl, FM_SUSPECT_FAULT_LIST, &nvp, &nvc) == 0 && nvc != 0) {
302 		while (nvc-- != 0) {
303 			if (nvlist_lookup_string(*nvp++, FM_CLASS, &p) == 0)
304 				fmd_dispq_dispatch(dqp, ep, p);
305 		}
306 	}
307 
308 	/*
309 	 * Once we've handled any recursive invocations, grab the dispatch
310 	 * queue lock and walk down the dispatch queue hashes posting the event
311 	 * for each subscriber.  If the total subscribers (n) is zero, send
312 	 * the event by default to the self-diagnosis module for handling.
313 	 */
314 	len = strlen(class);
315 	s = alloca(len + 1);
316 	(void) strcpy(s, class);
317 
318 	cv = alloca(sizeof (char *) * (len / 2 + 1));
319 	cc = 0;
320 
321 	for (p = strtok_r(s, ".", &q); p != NULL; p = strtok_r(NULL, ".", &q))
322 		cv[cc++] = p;
323 
324 	(void) pthread_rwlock_rdlock(&dqp->dq_lock);
325 	n = fmd_dispq_dispatchv(dqp->dq_root, ep, class, cc, cv);
326 	(void) pthread_rwlock_unlock(&dqp->dq_lock);
327 
328 	fmd_dprintf(FMD_DBG_DISP, "%s dispatched to %u modules\n", class, n);
329 
330 	if (n == 0 && fmd.d_self != NULL)
331 		fmd_eventq_insert_at_time(fmd.d_self->mod_queue, ep);
332 
333 	fmd_event_rele(ep);
334 }
335