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
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.
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]
24 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
25 * Use is subject to license terms.
28 #pragma ident "%Z%%M% %I% %E% SMI"
30 #include <fmd_ustat.h>
31 #include <fmd_alloc.h>
33 #include <fmd_string.h>
34 #include <fmd_error.h>
37 static fmd_ustat_chunk_t
*
38 fmd_ustat_chunk_init(fmd_ustat_t
*usp
, fmd_stat_t
*base
, uint_t len
)
40 fmd_ustat_chunk_t
*cp
;
42 cp
= fmd_zalloc(sizeof (fmd_ustat_chunk_t
), FMD_SLEEP
);
47 ASSERT(RW_WRITE_HELD(&usp
->us_lock
));
48 fmd_list_append(&usp
->us_chunks
, cp
);
54 fmd_ustat_chunk_hold(fmd_ustat_t
*usp
, fmd_ustat_chunk_t
*cp
)
56 ASSERT(RW_WRITE_HELD(&usp
->us_lock
));
58 ASSERT(cp
->usc_refs
!= 0);
62 fmd_ustat_chunk_rele(fmd_ustat_t
*usp
, fmd_ustat_chunk_t
*cp
)
64 ASSERT(RW_WRITE_HELD(&usp
->us_lock
));
65 ASSERT(cp
->usc_refs
!= 0);
67 if (--cp
->usc_refs
== 0) {
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.
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
));
80 fmd_ustat_create(void)
82 fmd_ustat_t
*usp
= fmd_zalloc(sizeof (fmd_ustat_t
), FMD_SLEEP
);
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
);
92 fmd_ustat_destroy(fmd_ustat_t
*usp
)
94 fmd_ustat_elem_t
*ep
, *np
;
97 (void) pthread_rwlock_wrlock(&usp
->us_lock
);
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
);
104 if (ep
->use_chunk
!= NULL
)
105 fmd_ustat_chunk_rele(usp
, ep
->use_chunk
);
108 fmd_free(ep
, sizeof (fmd_ustat_elem_t
));
112 ASSERT(usp
->us_chunks
.l_next
== NULL
);
113 ASSERT(usp
->us_chunks
.l_prev
== NULL
);
115 fmd_free(usp
->us_hash
, sizeof (void *) * usp
->us_hashlen
);
116 fmd_free(usp
, sizeof (fmd_ustat_t
));
120 fmd_ustat_snapshot(fmd_ustat_t
*usp
, fmd_ustat_snap_t
*uss
)
122 const fmd_ustat_elem_t
*ep
;
126 (void) pthread_rwlock_wrlock(&usp
->us_lock
);
128 uss
->uss_buf
= sp
= malloc(sizeof (fmd_stat_t
) * usp
->us_nelems
);
129 uss
->uss_len
= usp
->us_nelems
;
131 if (uss
->uss_buf
== NULL
) {
132 (void) pthread_rwlock_unlock(&usp
->us_lock
);
133 return (fmd_set_errno(EFMD_STAT_NOMEM
));
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
);
146 ASSERT(sp
== uss
->uss_buf
+ uss
->uss_len
);
147 (void) pthread_rwlock_unlock(&usp
->us_lock
);
152 fmd_ustat_delete_locked(fmd_ustat_t
*usp
, uint_t n
, fmd_stat_t
*sp
, int strfree
)
154 ASSERT(RW_WRITE_HELD(&usp
->us_lock
));
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
];
160 for (ep
= *pp
; ep
!= NULL
; ep
= ep
->use_next
) {
161 if (strcmp(sp
->fmds_name
, ep
->use_stat
->fmds_name
) != 0)
168 continue; /* silently ignore unregistered entries */
170 if (strfree
&& ep
->use_stat
->fmds_type
== FMD_TYPE_STRING
)
171 fmd_strfree(ep
->use_stat
->fmds_value
.str
);
173 if (ep
->use_chunk
!= NULL
)
174 fmd_ustat_chunk_rele(usp
, ep
->use_chunk
);
177 fmd_free(ep
, sizeof (fmd_ustat_elem_t
));
183 fmd_ustat_insert(fmd_ustat_t
*usp
, uint_t flags
,
184 uint_t n
, fmd_stat_t
*template, fmd_stat_t
**epp
)
186 fmd_stat_t
*stats
, *sp
;
187 fmd_ustat_chunk_t
*cp
;
190 int checkid
= flags
& FMD_USTAT_VALIDATE
;
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
);
198 sp
= stats
= template;
200 (void) pthread_rwlock_wrlock(&usp
->us_lock
);
202 if (flags
& FMD_USTAT_ALLOC
)
203 cp
= fmd_ustat_chunk_init(usp
, stats
, n
);
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
;
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.
217 for (p
= sp
->fmds_name
; p
< q
; p
++) {
223 q
[-1] = '\0'; /* nul-terminate for subsequent message */
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
);
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
);
239 if (sp
->fmds_type
== FMD_TYPE_STRING
)
240 has_str
++; /* flag for second pass; see below */
242 h
= fmd_strhash(sp
->fmds_name
) % usp
->us_hashlen
;
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)
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
);
256 ep
= fmd_alloc(sizeof (fmd_ustat_elem_t
), FMD_SLEEP
);
258 ep
->use_next
= usp
->us_hash
[h
];
259 usp
->us_hash
[h
] = ep
;
264 fmd_ustat_chunk_hold(usp
, cp
);
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()).
277 fmd_ustat_delete_locked(usp
, i
, stats
, FMD_B_FALSE
);
278 ASSERT(cp
== NULL
|| cp
->usc_refs
== 1);
282 } else if (has_str
) {
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.
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
);
298 fmd_ustat_chunk_rele(usp
, cp
);
300 (void) pthread_rwlock_unlock(&usp
->us_lock
);
301 return (err
? NULL
: stats
);
305 fmd_ustat_delete(fmd_ustat_t
*usp
, uint_t n
, fmd_stat_t
*sp
)
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
);
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.
317 fmd_ustat_delete_references(fmd_ustat_t
*usp
)
319 fmd_ustat_elem_t
*ep
, **pp
;
322 (void) pthread_rwlock_wrlock(&usp
->us_lock
);
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
) {
331 if (ep
->use_stat
->fmds_type
== FMD_TYPE_STRING
)
332 fmd_strfree(ep
->use_stat
->fmds_value
.str
);
335 fmd_free(ep
, sizeof (fmd_ustat_elem_t
));
340 (void) pthread_rwlock_unlock(&usp
->us_lock
);