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