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 /* 24 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 25 * Use is subject to license terms. 26 */ 27 28 #include <fmd_ustat.h> 29 #include <fmd_alloc.h> 30 #include <fmd_subr.h> 31 #include <fmd_string.h> 32 #include <fmd_error.h> 33 #include <fmd.h> 34 35 static fmd_ustat_chunk_t * 36 fmd_ustat_chunk_init(fmd_ustat_t *usp, fmd_stat_t *base, uint_t len) 37 { 38 fmd_ustat_chunk_t *cp; 39 40 cp = fmd_zalloc(sizeof (fmd_ustat_chunk_t), FMD_SLEEP); 41 cp->usc_base = base; 42 cp->usc_len = len; 43 cp->usc_refs = 1; 44 45 ASSERT(RW_WRITE_HELD(&usp->us_lock)); 46 fmd_list_append(&usp->us_chunks, cp); 47 48 return (cp); 49 } 50 51 static void 52 fmd_ustat_chunk_hold(fmd_ustat_t *usp, fmd_ustat_chunk_t *cp) 53 { 54 ASSERT(RW_WRITE_HELD(&usp->us_lock)); 55 cp->usc_refs++; 56 ASSERT(cp->usc_refs != 0); 57 } 58 59 static void 60 fmd_ustat_chunk_rele(fmd_ustat_t *usp, fmd_ustat_chunk_t *cp) 61 { 62 ASSERT(RW_WRITE_HELD(&usp->us_lock)); 63 ASSERT(cp->usc_refs != 0); 64 65 if (--cp->usc_refs == 0) { 66 /* 67 * Note that any strings pointed to by FMD_TYPE_STRING stats 68 * are freed one-by-one before releasing the chunk. So here 69 * we can just free the chunk and not worry about its content. 70 */ 71 fmd_free(cp->usc_base, sizeof (fmd_stat_t) * cp->usc_len); 72 fmd_list_delete(&usp->us_chunks, cp); 73 fmd_free(cp, sizeof (fmd_ustat_chunk_t)); 74 } 75 } 76 77 fmd_ustat_t * 78 fmd_ustat_create(void) 79 { 80 fmd_ustat_t *usp = fmd_zalloc(sizeof (fmd_ustat_t), FMD_SLEEP); 81 82 (void) pthread_rwlock_init(&usp->us_lock, NULL); 83 usp->us_hashlen = fmd.d_str_buckets; 84 usp->us_hash = fmd_zalloc(sizeof (void *) * usp->us_hashlen, FMD_SLEEP); 85 86 return (usp); 87 } 88 89 void 90 fmd_ustat_destroy(fmd_ustat_t *usp) 91 { 92 fmd_ustat_elem_t *ep, *np; 93 uint_t i; 94 95 (void) pthread_rwlock_wrlock(&usp->us_lock); 96 97 for (i = 0; i < usp->us_hashlen; i++) { 98 for (ep = usp->us_hash[i]; ep != NULL; ep = np) { 99 if (ep->use_stat->fmds_type == FMD_TYPE_STRING) 100 fmd_strfree(ep->use_stat->fmds_value.str); 101 102 if (ep->use_chunk != NULL) 103 fmd_ustat_chunk_rele(usp, ep->use_chunk); 104 105 np = ep->use_next; 106 fmd_free(ep, sizeof (fmd_ustat_elem_t)); 107 } 108 } 109 110 ASSERT(usp->us_chunks.l_next == NULL); 111 ASSERT(usp->us_chunks.l_prev == NULL); 112 113 fmd_free(usp->us_hash, sizeof (void *) * usp->us_hashlen); 114 fmd_free(usp, sizeof (fmd_ustat_t)); 115 } 116 117 int 118 fmd_ustat_snapshot(fmd_ustat_t *usp, fmd_ustat_snap_t *uss) 119 { 120 const fmd_ustat_elem_t *ep; 121 fmd_stat_t *sp; 122 uint_t i; 123 124 (void) pthread_rwlock_wrlock(&usp->us_lock); 125 126 uss->uss_buf = sp = malloc(sizeof (fmd_stat_t) * usp->us_nelems); 127 uss->uss_len = usp->us_nelems; 128 129 if (uss->uss_buf == NULL) { 130 (void) pthread_rwlock_unlock(&usp->us_lock); 131 return (fmd_set_errno(EFMD_STAT_NOMEM)); 132 } 133 134 for (i = 0; i < usp->us_hashlen; i++) { 135 for (ep = usp->us_hash[i]; ep != NULL; ep = ep->use_next) { 136 bcopy(ep->use_stat, sp, sizeof (fmd_stat_t)); 137 if (sp->fmds_type == FMD_TYPE_STRING && 138 sp->fmds_value.str != NULL) 139 sp->fmds_value.str = strdup(sp->fmds_value.str); 140 sp++; 141 } 142 } 143 144 ASSERT(sp == uss->uss_buf + uss->uss_len); 145 (void) pthread_rwlock_unlock(&usp->us_lock); 146 return (0); 147 } 148 149 static void 150 fmd_ustat_delete_locked(fmd_ustat_t *usp, uint_t n, fmd_stat_t *sp, int strfree) 151 { 152 ASSERT(RW_WRITE_HELD(&usp->us_lock)); 153 154 for (; n-- != 0; sp++) { 155 uint_t h = fmd_strhash(sp->fmds_name) % usp->us_hashlen; 156 fmd_ustat_elem_t *ep, **pp = &usp->us_hash[h]; 157 158 for (ep = *pp; ep != NULL; ep = ep->use_next) { 159 if (strcmp(sp->fmds_name, ep->use_stat->fmds_name) != 0) 160 pp = &ep->use_next; 161 else 162 break; 163 } 164 165 if (ep == NULL) 166 continue; /* silently ignore unregistered entries */ 167 168 if (strfree && ep->use_stat->fmds_type == FMD_TYPE_STRING) 169 fmd_strfree(ep->use_stat->fmds_value.str); 170 171 if (ep->use_chunk != NULL) 172 fmd_ustat_chunk_rele(usp, ep->use_chunk); 173 174 *pp = ep->use_next; 175 fmd_free(ep, sizeof (fmd_ustat_elem_t)); 176 usp->us_nelems--; 177 } 178 } 179 180 fmd_stat_t * 181 fmd_ustat_insert(fmd_ustat_t *usp, uint_t flags, 182 uint_t n, fmd_stat_t *template, fmd_stat_t **epp) 183 { 184 fmd_stat_t *stats, *sp; 185 fmd_ustat_chunk_t *cp; 186 uint_t i; 187 188 int checkid = flags & FMD_USTAT_VALIDATE; 189 int has_str = 0; 190 int err = 0; 191 192 if (flags & FMD_USTAT_ALLOC) { 193 sp = stats = fmd_alloc(sizeof (fmd_stat_t) * n, FMD_SLEEP); 194 bcopy(template, stats, sizeof (fmd_stat_t) * n); 195 } else 196 sp = stats = template; 197 198 (void) pthread_rwlock_wrlock(&usp->us_lock); 199 200 if (flags & FMD_USTAT_ALLOC) 201 cp = fmd_ustat_chunk_init(usp, stats, n); 202 else 203 cp = NULL; 204 205 for (i = 0; i < n; i++, sp++) { 206 char *p, *q = sp->fmds_name + sizeof (sp->fmds_name); 207 fmd_ustat_elem_t *ep; 208 uint_t h; 209 210 /* 211 * Since a module may be passing in this statistic and our 212 * names are represented by a fixed-size array, scan fmds_name 213 * to ensure it has a \0 somewhere before we attempt strcmps. 214 */ 215 for (p = sp->fmds_name; p < q; p++) { 216 if (*p == '\0') 217 break; 218 } 219 220 if (p == q) 221 q[-1] = '\0'; /* nul-terminate for subsequent message */ 222 223 if (p == q || fmd_strbadid(sp->fmds_name, checkid) != NULL) { 224 fmd_error(EFMD_STAT_BADNAME, "'%s' does not conform to " 225 "statistic naming rules\n", sp->fmds_name); 226 err = fmd_set_errno(EFMD_STAT_BADNAME); 227 break; 228 } 229 230 if (sp->fmds_type > FMD_TYPE_SIZE) { 231 fmd_error(EFMD_STAT_BADTYPE, "'%s' statistic type %u " 232 "is not valid\n", sp->fmds_name, sp->fmds_type); 233 err = fmd_set_errno(EFMD_STAT_BADTYPE); 234 break; 235 } 236 237 if (sp->fmds_type == FMD_TYPE_STRING) 238 has_str++; /* flag for second pass; see below */ 239 240 h = fmd_strhash(sp->fmds_name) % usp->us_hashlen; 241 242 for (ep = usp->us_hash[h]; ep != NULL; ep = ep->use_next) { 243 if (strcmp(sp->fmds_name, ep->use_stat->fmds_name) == 0) 244 break; 245 } 246 247 if (ep != NULL) { 248 fmd_error(EFMD_STAT_DUPNAME, "'%s' is already defined " 249 "as a statistic name\n", sp->fmds_name); 250 err = fmd_set_errno(EFMD_STAT_DUPNAME); 251 break; 252 } 253 254 ep = fmd_alloc(sizeof (fmd_ustat_elem_t), FMD_SLEEP); 255 256 ep->use_next = usp->us_hash[h]; 257 usp->us_hash[h] = ep; 258 ep->use_stat = sp; 259 ep->use_chunk = cp; 260 261 if (cp != NULL) 262 fmd_ustat_chunk_hold(usp, cp); 263 264 usp->us_nelems++; 265 } 266 267 /* 268 * If an error occurred, delete all the stats inserted by successful 269 * iterations of the loop [0 .. i-1]. If 'epp' is non-NULL, store a 270 * copy of the input stat pointer that caused the error there. When 271 * the delete is done, if we allocated a chunk, there should be only 272 * one reference remaining (from the initial fmd_ustat_chunk_init()). 273 */ 274 if (err != 0) { 275 fmd_ustat_delete_locked(usp, i, stats, FMD_B_FALSE); 276 ASSERT(cp == NULL || cp->usc_refs == 1); 277 if (epp != NULL) 278 *epp = template + i; 279 280 } else if (has_str) { 281 /* 282 * If no error occurred and one or more string stats are being 283 * inserted, make a second pass through 'stats' duplicating any 284 * initial strings so that fmd_stat_setstr() can alloc/free. 285 */ 286 for (sp = stats, i = 0; i < n; i++, sp++) { 287 if (sp->fmds_type == FMD_TYPE_STRING && 288 sp->fmds_value.str != NULL) { 289 sp->fmds_value.str = fmd_strdup( 290 sp->fmds_value.str, FMD_SLEEP); 291 } 292 } 293 } 294 295 if (cp != NULL) 296 fmd_ustat_chunk_rele(usp, cp); 297 298 (void) pthread_rwlock_unlock(&usp->us_lock); 299 return (err ? NULL : stats); 300 } 301 302 void 303 fmd_ustat_delete(fmd_ustat_t *usp, uint_t n, fmd_stat_t *sp) 304 { 305 (void) pthread_rwlock_wrlock(&usp->us_lock); 306 fmd_ustat_delete_locked(usp, n, sp, FMD_B_TRUE); 307 (void) pthread_rwlock_unlock(&usp->us_lock); 308 } 309 310 /* 311 * Delete all statistics that are references to external memory (that is, all 312 * statistics inserted with FMD_STAT_NOALLOC), i.e. a NULL ep->use_chunk. 313 */ 314 void 315 fmd_ustat_delete_references(fmd_ustat_t *usp) 316 { 317 fmd_ustat_elem_t *ep, **pp; 318 uint_t i; 319 320 (void) pthread_rwlock_wrlock(&usp->us_lock); 321 322 for (i = 0; i < usp->us_hashlen; i++) { 323 for (pp = &usp->us_hash[i], ep = *pp; ep != NULL; ep = *pp) { 324 if (ep->use_chunk != NULL) { 325 pp = &ep->use_next; 326 continue; 327 } 328 329 if (ep->use_stat->fmds_type == FMD_TYPE_STRING) 330 fmd_strfree(ep->use_stat->fmds_value.str); 331 332 *pp = ep->use_next; 333 fmd_free(ep, sizeof (fmd_ustat_elem_t)); 334 usp->us_nelems--; 335 } 336 } 337 338 (void) pthread_rwlock_unlock(&usp->us_lock); 339 } 340