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