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