]>
Commit | Line | Data |
---|---|---|
4b393c50 | 1 | /* |
716154c5 BB |
2 | * Copyright (C) 2007-2010 Lawrence Livermore National Security, LLC. |
3 | * Copyright (C) 2007 The Regents of the University of California. | |
4 | * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). | |
5 | * Written by Brian Behlendorf <behlendorf1@llnl.gov>. | |
715f6251 BB |
6 | * UCRL-CODE-235197 |
7 | * | |
716154c5 | 8 | * This file is part of the SPL, Solaris Porting Layer. |
716154c5 BB |
9 | * |
10 | * The SPL is free software; you can redistribute it and/or modify it | |
11 | * under the terms of the GNU General Public License as published by the | |
12 | * Free Software Foundation; either version 2 of the License, or (at your | |
13 | * option) any later version. | |
715f6251 | 14 | * |
716154c5 | 15 | * The SPL is distributed in the hope that it will be useful, but WITHOUT |
715f6251 BB |
16 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
17 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
18 | * for more details. | |
19 | * | |
20 | * You should have received a copy of the GNU General Public License along | |
716154c5 | 21 | * with the SPL. If not, see <http://www.gnu.org/licenses/>. |
5461eefe | 22 | * |
716154c5 | 23 | * Solaris Porting Layer (SPL) Kstat Implementation. |
6fffc88b SK |
24 | * |
25 | * Links to Illumos.org for more information on kstat function: | |
26 | * [1] https://illumos.org/man/1M/kstat | |
27 | * [2] https://illumos.org/man/9f/kstat_create | |
4b393c50 | 28 | */ |
715f6251 | 29 | |
ae4c36ad | 30 | #include <linux/seq_file.h> |
55abb092 | 31 | #include <sys/kstat.h> |
e5b9b344 | 32 | #include <sys/vmem.h> |
cd478018 | 33 | #include <sys/cmn_err.h> |
a9125891 | 34 | #include <sys/sysmacros.h> |
84980ee0 | 35 | #include <sys/string.h> |
04a479f7 | 36 | |
f2a745c4 RY |
37 | static kmutex_t kstat_module_lock; |
38 | static struct list_head kstat_module_list; | |
04a479f7 BB |
39 | static kid_t kstat_id; |
40 | ||
56d40a68 PS |
41 | static int |
42 | kstat_resize_raw(kstat_t *ksp) | |
43 | { | |
44 | if (ksp->ks_raw_bufsize == KSTAT_RAW_MAX) | |
5461eefe | 45 | return (ENOMEM); |
56d40a68 PS |
46 | |
47 | vmem_free(ksp->ks_raw_buf, ksp->ks_raw_bufsize); | |
48 | ksp->ks_raw_bufsize = MIN(ksp->ks_raw_bufsize * 2, KSTAT_RAW_MAX); | |
49 | ksp->ks_raw_buf = vmem_alloc(ksp->ks_raw_bufsize, KM_SLEEP); | |
50 | ||
5461eefe | 51 | return (0); |
56d40a68 PS |
52 | } |
53 | ||
54 | static int | |
04a479f7 BB |
55 | kstat_seq_show_headers(struct seq_file *f) |
56 | { | |
5461eefe | 57 | kstat_t *ksp = (kstat_t *)f->private; |
56d40a68 PS |
58 | int rc = 0; |
59 | ||
5461eefe | 60 | ASSERT(ksp->ks_magic == KS_MAGIC); |
04a479f7 | 61 | |
5461eefe BB |
62 | seq_printf(f, "%d %d 0x%02x %d %d %lld %lld\n", |
63 | ksp->ks_kid, ksp->ks_type, ksp->ks_flags, | |
64 | ksp->ks_ndata, (int)ksp->ks_data_size, | |
65 | ksp->ks_crtime, ksp->ks_snaptime); | |
04a479f7 BB |
66 | |
67 | switch (ksp->ks_type) { | |
5461eefe | 68 | case KSTAT_TYPE_RAW: |
56d40a68 | 69 | restart: |
5461eefe BB |
70 | if (ksp->ks_raw_ops.headers) { |
71 | rc = ksp->ks_raw_ops.headers( | |
72 | ksp->ks_raw_buf, ksp->ks_raw_bufsize); | |
56d40a68 PS |
73 | if (rc == ENOMEM && !kstat_resize_raw(ksp)) |
74 | goto restart; | |
75 | if (!rc) | |
5461eefe BB |
76 | seq_puts(f, ksp->ks_raw_buf); |
77 | } else { | |
78 | seq_printf(f, "raw data\n"); | |
79 | } | |
80 | break; | |
81 | case KSTAT_TYPE_NAMED: | |
82 | seq_printf(f, "%-31s %-4s %s\n", | |
83 | "name", "type", "data"); | |
84 | break; | |
85 | case KSTAT_TYPE_INTR: | |
86 | seq_printf(f, "%-8s %-8s %-8s %-8s %-8s\n", | |
87 | "hard", "soft", "watchdog", | |
88 | "spurious", "multsvc"); | |
89 | break; | |
90 | case KSTAT_TYPE_IO: | |
91 | seq_printf(f, | |
92 | "%-8s %-8s %-8s %-8s %-8s %-8s " | |
93 | "%-8s %-8s %-8s %-8s %-8s %-8s\n", | |
94 | "nread", "nwritten", "reads", "writes", | |
95 | "wtime", "wlentime", "wupdate", | |
96 | "rtime", "rlentime", "rupdate", | |
97 | "wcnt", "rcnt"); | |
98 | break; | |
99 | case KSTAT_TYPE_TIMER: | |
100 | seq_printf(f, | |
101 | "%-31s %-8s " | |
102 | "%-8s %-8s %-8s %-8s %-8s\n", | |
103 | "name", "events", "elapsed", | |
104 | "min", "max", "start", "stop"); | |
105 | break; | |
106 | default: | |
107 | PANIC("Undefined kstat type %d\n", ksp->ks_type); | |
108 | } | |
109 | ||
110 | return (-rc); | |
04a479f7 BB |
111 | } |
112 | ||
113 | static int | |
114 | kstat_seq_show_raw(struct seq_file *f, unsigned char *p, int l) | |
115 | { | |
5461eefe | 116 | int i, j; |
04a479f7 | 117 | |
5461eefe BB |
118 | for (i = 0; ; i++) { |
119 | seq_printf(f, "%03x:", i); | |
04a479f7 | 120 | |
5461eefe BB |
121 | for (j = 0; j < 16; j++) { |
122 | if (i * 16 + j >= l) { | |
123 | seq_printf(f, "\n"); | |
124 | goto out; | |
125 | } | |
04a479f7 | 126 | |
5461eefe BB |
127 | seq_printf(f, " %02x", (unsigned char)p[i * 16 + j]); |
128 | } | |
129 | seq_printf(f, "\n"); | |
130 | } | |
04a479f7 | 131 | out: |
5461eefe | 132 | return (0); |
04a479f7 BB |
133 | } |
134 | ||
135 | static int | |
136 | kstat_seq_show_named(struct seq_file *f, kstat_named_t *knp) | |
137 | { | |
5461eefe BB |
138 | seq_printf(f, "%-31s %-4d ", knp->name, knp->data_type); |
139 | ||
140 | switch (knp->data_type) { | |
141 | case KSTAT_DATA_CHAR: | |
142 | knp->value.c[15] = '\0'; /* NULL terminate */ | |
143 | seq_printf(f, "%-16s", knp->value.c); | |
144 | break; | |
145 | /* | |
146 | * NOTE - We need to be more careful able what tokens are | |
147 | * used for each arch, for now this is correct for x86_64. | |
148 | */ | |
149 | case KSTAT_DATA_INT32: | |
150 | seq_printf(f, "%d", knp->value.i32); | |
151 | break; | |
152 | case KSTAT_DATA_UINT32: | |
153 | seq_printf(f, "%u", knp->value.ui32); | |
154 | break; | |
155 | case KSTAT_DATA_INT64: | |
156 | seq_printf(f, "%lld", (signed long long)knp->value.i64); | |
157 | break; | |
158 | case KSTAT_DATA_UINT64: | |
159 | seq_printf(f, "%llu", | |
160 | (unsigned long long)knp->value.ui64); | |
161 | break; | |
162 | case KSTAT_DATA_LONG: | |
163 | seq_printf(f, "%ld", knp->value.l); | |
164 | break; | |
165 | case KSTAT_DATA_ULONG: | |
166 | seq_printf(f, "%lu", knp->value.ul); | |
167 | break; | |
168 | case KSTAT_DATA_STRING: | |
169 | KSTAT_NAMED_STR_PTR(knp) | |
170 | [KSTAT_NAMED_STR_BUFLEN(knp)-1] = '\0'; | |
171 | seq_printf(f, "%s", KSTAT_NAMED_STR_PTR(knp)); | |
172 | break; | |
173 | default: | |
174 | PANIC("Undefined kstat data type %d\n", knp->data_type); | |
175 | } | |
176 | ||
177 | seq_printf(f, "\n"); | |
178 | ||
179 | return (0); | |
04a479f7 BB |
180 | } |
181 | ||
182 | static int | |
183 | kstat_seq_show_intr(struct seq_file *f, kstat_intr_t *kip) | |
184 | { | |
5461eefe BB |
185 | seq_printf(f, "%-8u %-8u %-8u %-8u %-8u\n", |
186 | kip->intrs[KSTAT_INTR_HARD], | |
187 | kip->intrs[KSTAT_INTR_SOFT], | |
188 | kip->intrs[KSTAT_INTR_WATCHDOG], | |
189 | kip->intrs[KSTAT_INTR_SPURIOUS], | |
190 | kip->intrs[KSTAT_INTR_MULTSVC]); | |
191 | ||
192 | return (0); | |
04a479f7 BB |
193 | } |
194 | ||
195 | static int | |
196 | kstat_seq_show_io(struct seq_file *f, kstat_io_t *kip) | |
197 | { | |
a48cd034 | 198 | /* though wlentime & friends are signed, they will never be negative */ |
5461eefe | 199 | seq_printf(f, |
a48cd034 RE |
200 | "%-8llu %-8llu %-8u %-8u %-8llu %-8llu " |
201 | "%-8llu %-8llu %-8llu %-8llu %-8u %-8u\n", | |
5461eefe BB |
202 | kip->nread, kip->nwritten, |
203 | kip->reads, kip->writes, | |
204 | kip->wtime, kip->wlentime, kip->wlastupdate, | |
205 | kip->rtime, kip->rlentime, kip->rlastupdate, | |
206 | kip->wcnt, kip->rcnt); | |
207 | ||
208 | return (0); | |
04a479f7 BB |
209 | } |
210 | ||
211 | static int | |
212 | kstat_seq_show_timer(struct seq_file *f, kstat_timer_t *ktp) | |
213 | { | |
5461eefe | 214 | seq_printf(f, |
a48cd034 | 215 | "%-31s %-8llu %-8llu %-8llu %-8llu %-8llu %-8llu\n", |
5461eefe BB |
216 | ktp->name, ktp->num_events, ktp->elapsed_time, |
217 | ktp->min_time, ktp->max_time, | |
218 | ktp->start_time, ktp->stop_time); | |
04a479f7 | 219 | |
5461eefe | 220 | return (0); |
04a479f7 BB |
221 | } |
222 | ||
223 | static int | |
224 | kstat_seq_show(struct seq_file *f, void *p) | |
225 | { | |
5461eefe BB |
226 | kstat_t *ksp = (kstat_t *)f->private; |
227 | int rc = 0; | |
04a479f7 | 228 | |
5461eefe | 229 | ASSERT(ksp->ks_magic == KS_MAGIC); |
04a479f7 BB |
230 | |
231 | switch (ksp->ks_type) { | |
5461eefe | 232 | case KSTAT_TYPE_RAW: |
56d40a68 | 233 | restart: |
5461eefe BB |
234 | if (ksp->ks_raw_ops.data) { |
235 | rc = ksp->ks_raw_ops.data( | |
56d40a68 PS |
236 | ksp->ks_raw_buf, ksp->ks_raw_bufsize, p); |
237 | if (rc == ENOMEM && !kstat_resize_raw(ksp)) | |
238 | goto restart; | |
239 | if (!rc) | |
5461eefe BB |
240 | seq_puts(f, ksp->ks_raw_buf); |
241 | } else { | |
242 | ASSERT(ksp->ks_ndata == 1); | |
243 | rc = kstat_seq_show_raw(f, ksp->ks_data, | |
3673d032 | 244 | ksp->ks_data_size); |
5461eefe BB |
245 | } |
246 | break; | |
247 | case KSTAT_TYPE_NAMED: | |
248 | rc = kstat_seq_show_named(f, (kstat_named_t *)p); | |
249 | break; | |
250 | case KSTAT_TYPE_INTR: | |
251 | rc = kstat_seq_show_intr(f, (kstat_intr_t *)p); | |
252 | break; | |
253 | case KSTAT_TYPE_IO: | |
254 | rc = kstat_seq_show_io(f, (kstat_io_t *)p); | |
255 | break; | |
256 | case KSTAT_TYPE_TIMER: | |
257 | rc = kstat_seq_show_timer(f, (kstat_timer_t *)p); | |
258 | break; | |
259 | default: | |
260 | PANIC("Undefined kstat type %d\n", ksp->ks_type); | |
261 | } | |
262 | ||
263 | return (-rc); | |
04a479f7 BB |
264 | } |
265 | ||
68386b05 | 266 | static int |
9a8b7a74 BB |
267 | kstat_default_update(kstat_t *ksp, int rw) |
268 | { | |
269 | ASSERT(ksp != NULL); | |
56d40a68 PS |
270 | |
271 | if (rw == KSTAT_WRITE) | |
272 | return (EACCES); | |
273 | ||
5461eefe | 274 | return (0); |
9a8b7a74 BB |
275 | } |
276 | ||
04a479f7 BB |
277 | static void * |
278 | kstat_seq_data_addr(kstat_t *ksp, loff_t n) | |
279 | { | |
5461eefe | 280 | void *rc = NULL; |
04a479f7 BB |
281 | |
282 | switch (ksp->ks_type) { | |
5461eefe BB |
283 | case KSTAT_TYPE_RAW: |
284 | if (ksp->ks_raw_ops.addr) | |
285 | rc = ksp->ks_raw_ops.addr(ksp, n); | |
286 | else | |
287 | rc = ksp->ks_data; | |
288 | break; | |
289 | case KSTAT_TYPE_NAMED: | |
290 | rc = ksp->ks_data + n * sizeof (kstat_named_t); | |
291 | break; | |
292 | case KSTAT_TYPE_INTR: | |
293 | rc = ksp->ks_data + n * sizeof (kstat_intr_t); | |
294 | break; | |
295 | case KSTAT_TYPE_IO: | |
296 | rc = ksp->ks_data + n * sizeof (kstat_io_t); | |
297 | break; | |
298 | case KSTAT_TYPE_TIMER: | |
299 | rc = ksp->ks_data + n * sizeof (kstat_timer_t); | |
300 | break; | |
301 | default: | |
302 | PANIC("Undefined kstat type %d\n", ksp->ks_type); | |
303 | } | |
304 | ||
305 | return (rc); | |
04a479f7 BB |
306 | } |
307 | ||
308 | static void * | |
309 | kstat_seq_start(struct seq_file *f, loff_t *pos) | |
310 | { | |
5461eefe BB |
311 | loff_t n = *pos; |
312 | kstat_t *ksp = (kstat_t *)f->private; | |
313 | ASSERT(ksp->ks_magic == KS_MAGIC); | |
04a479f7 | 314 | |
ffbf0e57 | 315 | mutex_enter(ksp->ks_lock); |
71c9f0b0 | 316 | |
5461eefe BB |
317 | if (ksp->ks_type == KSTAT_TYPE_RAW) { |
318 | ksp->ks_raw_bufsize = PAGE_SIZE; | |
319 | ksp->ks_raw_buf = vmem_alloc(ksp->ks_raw_bufsize, KM_SLEEP); | |
320 | } | |
56d40a68 | 321 | |
5461eefe BB |
322 | /* Dynamically update kstat, on error existing kstats are used */ |
323 | (void) ksp->ks_update(ksp, KSTAT_READ); | |
9a8b7a74 | 324 | |
04a479f7 BB |
325 | ksp->ks_snaptime = gethrtime(); |
326 | ||
f0ed6c74 TH |
327 | if (!(ksp->ks_flags & KSTAT_FLAG_NO_HEADERS) && !n && |
328 | kstat_seq_show_headers(f)) | |
8d9a23e8 | 329 | return (NULL); |
04a479f7 | 330 | |
5461eefe BB |
331 | if (n >= ksp->ks_ndata) |
332 | return (NULL); | |
04a479f7 | 333 | |
5461eefe | 334 | return (kstat_seq_data_addr(ksp, n)); |
04a479f7 BB |
335 | } |
336 | ||
337 | static void * | |
338 | kstat_seq_next(struct seq_file *f, void *p, loff_t *pos) | |
339 | { | |
5461eefe BB |
340 | kstat_t *ksp = (kstat_t *)f->private; |
341 | ASSERT(ksp->ks_magic == KS_MAGIC); | |
04a479f7 | 342 | |
5461eefe BB |
343 | ++*pos; |
344 | if (*pos >= ksp->ks_ndata) | |
345 | return (NULL); | |
04a479f7 | 346 | |
5461eefe | 347 | return (kstat_seq_data_addr(ksp, *pos)); |
04a479f7 BB |
348 | } |
349 | ||
350 | static void | |
351 | kstat_seq_stop(struct seq_file *f, void *v) | |
352 | { | |
ffbf0e57 CP |
353 | kstat_t *ksp = (kstat_t *)f->private; |
354 | ASSERT(ksp->ks_magic == KS_MAGIC); | |
04a479f7 | 355 | |
56d40a68 PS |
356 | if (ksp->ks_type == KSTAT_TYPE_RAW) |
357 | vmem_free(ksp->ks_raw_buf, ksp->ks_raw_bufsize); | |
358 | ||
ffbf0e57 | 359 | mutex_exit(ksp->ks_lock); |
04a479f7 BB |
360 | } |
361 | ||
18168da7 | 362 | static const struct seq_operations kstat_seq_ops = { |
5461eefe BB |
363 | .show = kstat_seq_show, |
364 | .start = kstat_seq_start, | |
365 | .next = kstat_seq_next, | |
366 | .stop = kstat_seq_stop, | |
04a479f7 BB |
367 | }; |
368 | ||
f2a745c4 RY |
369 | static kstat_module_t * |
370 | kstat_find_module(char *name) | |
371 | { | |
7cf1fe63 | 372 | kstat_module_t *module = NULL; |
f2a745c4 | 373 | |
3673d032 | 374 | list_for_each_entry(module, &kstat_module_list, ksm_module_list) { |
f2a745c4 RY |
375 | if (strncmp(name, module->ksm_name, KSTAT_STRLEN) == 0) |
376 | return (module); | |
3673d032 | 377 | } |
f2a745c4 RY |
378 | |
379 | return (NULL); | |
380 | } | |
381 | ||
382 | static kstat_module_t * | |
383 | kstat_create_module(char *name) | |
384 | { | |
385 | kstat_module_t *module; | |
386 | struct proc_dir_entry *pde; | |
387 | ||
388 | pde = proc_mkdir(name, proc_spl_kstat); | |
389 | if (pde == NULL) | |
390 | return (NULL); | |
391 | ||
392 | module = kmem_alloc(sizeof (kstat_module_t), KM_SLEEP); | |
393 | module->ksm_proc = pde; | |
7584fbe8 | 394 | strlcpy(module->ksm_name, name, KSTAT_STRLEN); |
f2a745c4 RY |
395 | INIT_LIST_HEAD(&module->ksm_kstat_list); |
396 | list_add_tail(&module->ksm_module_list, &kstat_module_list); | |
397 | ||
398 | return (module); | |
399 | ||
400 | } | |
401 | ||
402 | static void | |
403 | kstat_delete_module(kstat_module_t *module) | |
404 | { | |
405 | ASSERT(list_empty(&module->ksm_kstat_list)); | |
406 | remove_proc_entry(module->ksm_name, proc_spl_kstat); | |
407 | list_del(&module->ksm_module_list); | |
5461eefe | 408 | kmem_free(module, sizeof (kstat_module_t)); |
f2a745c4 RY |
409 | } |
410 | ||
04a479f7 BB |
411 | static int |
412 | proc_kstat_open(struct inode *inode, struct file *filp) | |
413 | { | |
5461eefe BB |
414 | struct seq_file *f; |
415 | int rc; | |
04a479f7 | 416 | |
5461eefe BB |
417 | rc = seq_open(filp, &kstat_seq_ops); |
418 | if (rc) | |
419 | return (rc); | |
04a479f7 | 420 | |
5461eefe | 421 | f = filp->private_data; |
5dccd7e8 | 422 | f->private = SPL_PDE_DATA(inode); |
04a479f7 | 423 | |
a06ba74a | 424 | return (0); |
04a479f7 BB |
425 | } |
426 | ||
56d40a68 | 427 | static ssize_t |
5461eefe BB |
428 | proc_kstat_write(struct file *filp, const char __user *buf, size_t len, |
429 | loff_t *ppos) | |
56d40a68 PS |
430 | { |
431 | struct seq_file *f = filp->private_data; | |
432 | kstat_t *ksp = f->private; | |
433 | int rc; | |
434 | ||
435 | ASSERT(ksp->ks_magic == KS_MAGIC); | |
436 | ||
437 | mutex_enter(ksp->ks_lock); | |
438 | rc = ksp->ks_update(ksp, KSTAT_WRITE); | |
439 | mutex_exit(ksp->ks_lock); | |
440 | ||
441 | if (rc) | |
442 | return (-rc); | |
443 | ||
444 | *ppos += len; | |
445 | return (len); | |
446 | } | |
447 | ||
0dd73648 BB |
448 | static const kstat_proc_op_t proc_kstat_operations = { |
449 | #ifdef HAVE_PROC_OPS_STRUCT | |
450 | .proc_open = proc_kstat_open, | |
451 | .proc_write = proc_kstat_write, | |
452 | .proc_read = seq_read, | |
453 | .proc_lseek = seq_lseek, | |
454 | .proc_release = seq_release, | |
455 | #else | |
56d40a68 PS |
456 | .open = proc_kstat_open, |
457 | .write = proc_kstat_write, | |
458 | .read = seq_read, | |
459 | .llseek = seq_lseek, | |
460 | .release = seq_release, | |
0dd73648 | 461 | #endif |
04a479f7 BB |
462 | }; |
463 | ||
56d40a68 PS |
464 | void |
465 | __kstat_set_raw_ops(kstat_t *ksp, | |
3673d032 BB |
466 | int (*headers)(char *buf, size_t size), |
467 | int (*data)(char *buf, size_t size, void *data), | |
468 | void *(*addr)(kstat_t *ksp, loff_t index)) | |
56d40a68 PS |
469 | { |
470 | ksp->ks_raw_ops.headers = headers; | |
471 | ksp->ks_raw_ops.data = data; | |
472 | ksp->ks_raw_ops.addr = addr; | |
473 | } | |
474 | EXPORT_SYMBOL(__kstat_set_raw_ops); | |
475 | ||
d1261452 JG |
476 | void |
477 | kstat_proc_entry_init(kstat_proc_entry_t *kpep, const char *module, | |
478 | const char *name) | |
479 | { | |
480 | kpep->kpe_owner = NULL; | |
481 | kpep->kpe_proc = NULL; | |
482 | INIT_LIST_HEAD(&kpep->kpe_list); | |
7584fbe8 RY |
483 | strlcpy(kpep->kpe_module, module, sizeof (kpep->kpe_module)); |
484 | strlcpy(kpep->kpe_name, name, sizeof (kpep->kpe_name)); | |
d1261452 JG |
485 | } |
486 | EXPORT_SYMBOL(kstat_proc_entry_init); | |
487 | ||
04a479f7 BB |
488 | kstat_t * |
489 | __kstat_create(const char *ks_module, int ks_instance, const char *ks_name, | |
5461eefe BB |
490 | const char *ks_class, uchar_t ks_type, uint_t ks_ndata, |
491 | uchar_t ks_flags) | |
04a479f7 BB |
492 | { |
493 | kstat_t *ksp; | |
494 | ||
495 | ASSERT(ks_module); | |
496 | ASSERT(ks_instance == 0); | |
497 | ASSERT(ks_name); | |
04a479f7 BB |
498 | |
499 | if ((ks_type == KSTAT_TYPE_INTR) || (ks_type == KSTAT_TYPE_IO)) | |
5461eefe | 500 | ASSERT(ks_ndata == 1); |
04a479f7 | 501 | |
5461eefe | 502 | ksp = kmem_zalloc(sizeof (*ksp), KM_SLEEP); |
04a479f7 | 503 | if (ksp == NULL) |
5461eefe | 504 | return (ksp); |
04a479f7 | 505 | |
f2a745c4 | 506 | mutex_enter(&kstat_module_lock); |
04a479f7 | 507 | ksp->ks_kid = kstat_id; |
5461eefe | 508 | kstat_id++; |
f2a745c4 | 509 | mutex_exit(&kstat_module_lock); |
04a479f7 | 510 | |
5461eefe | 511 | ksp->ks_magic = KS_MAGIC; |
ffbf0e57 CP |
512 | mutex_init(&ksp->ks_private_lock, NULL, MUTEX_DEFAULT, NULL); |
513 | ksp->ks_lock = &ksp->ks_private_lock; | |
04a479f7 BB |
514 | |
515 | ksp->ks_crtime = gethrtime(); | |
5461eefe | 516 | ksp->ks_snaptime = ksp->ks_crtime; |
04a479f7 | 517 | ksp->ks_instance = ks_instance; |
7584fbe8 | 518 | strlcpy(ksp->ks_class, ks_class, sizeof (ksp->ks_class)); |
04a479f7 BB |
519 | ksp->ks_type = ks_type; |
520 | ksp->ks_flags = ks_flags; | |
9a8b7a74 BB |
521 | ksp->ks_update = kstat_default_update; |
522 | ksp->ks_private = NULL; | |
56d40a68 PS |
523 | ksp->ks_raw_ops.headers = NULL; |
524 | ksp->ks_raw_ops.data = NULL; | |
525 | ksp->ks_raw_ops.addr = NULL; | |
526 | ksp->ks_raw_buf = NULL; | |
527 | ksp->ks_raw_bufsize = 0; | |
d1261452 | 528 | kstat_proc_entry_init(&ksp->ks_proc, ks_module, ks_name); |
04a479f7 BB |
529 | |
530 | switch (ksp->ks_type) { | |
5461eefe BB |
531 | case KSTAT_TYPE_RAW: |
532 | ksp->ks_ndata = 1; | |
533 | ksp->ks_data_size = ks_ndata; | |
534 | break; | |
535 | case KSTAT_TYPE_NAMED: | |
536 | ksp->ks_ndata = ks_ndata; | |
537 | ksp->ks_data_size = ks_ndata * sizeof (kstat_named_t); | |
538 | break; | |
539 | case KSTAT_TYPE_INTR: | |
540 | ksp->ks_ndata = ks_ndata; | |
541 | ksp->ks_data_size = ks_ndata * sizeof (kstat_intr_t); | |
542 | break; | |
543 | case KSTAT_TYPE_IO: | |
544 | ksp->ks_ndata = ks_ndata; | |
545 | ksp->ks_data_size = ks_ndata * sizeof (kstat_io_t); | |
546 | break; | |
547 | case KSTAT_TYPE_TIMER: | |
548 | ksp->ks_ndata = ks_ndata; | |
549 | ksp->ks_data_size = ks_ndata * sizeof (kstat_timer_t); | |
550 | break; | |
551 | default: | |
552 | PANIC("Undefined kstat type %d\n", ksp->ks_type); | |
553 | } | |
04a479f7 BB |
554 | |
555 | if (ksp->ks_flags & KSTAT_FLAG_VIRTUAL) { | |
5461eefe BB |
556 | ksp->ks_data = NULL; |
557 | } else { | |
558 | ksp->ks_data = kmem_zalloc(ksp->ks_data_size, KM_SLEEP); | |
559 | if (ksp->ks_data == NULL) { | |
560 | kmem_free(ksp, sizeof (*ksp)); | |
561 | ksp = NULL; | |
562 | } | |
563 | } | |
564 | ||
565 | return (ksp); | |
04a479f7 BB |
566 | } |
567 | EXPORT_SYMBOL(__kstat_create); | |
568 | ||
cd478018 | 569 | static int |
d1261452 | 570 | kstat_detect_collision(kstat_proc_entry_t *kpep) |
cd478018 | 571 | { |
572 | kstat_module_t *module; | |
7cf1fe63 | 573 | kstat_proc_entry_t *tmp = NULL; |
9df96926 | 574 | char *parent; |
cd478018 | 575 | char *cp; |
576 | ||
d1261452 | 577 | parent = kmem_asprintf("%s", kpep->kpe_module); |
cd478018 | 578 | |
9df96926 | 579 | if ((cp = strrchr(parent, '/')) == NULL) { |
e4f5fa12 | 580 | kmem_strfree(parent); |
cd478018 | 581 | return (0); |
9df96926 | 582 | } |
cd478018 | 583 | |
584 | cp[0] = '\0'; | |
585 | if ((module = kstat_find_module(parent)) != NULL) { | |
d1261452 JG |
586 | list_for_each_entry(tmp, &module->ksm_kstat_list, kpe_list) { |
587 | if (strncmp(tmp->kpe_name, cp+1, KSTAT_STRLEN) == 0) { | |
e4f5fa12 | 588 | kmem_strfree(parent); |
cd478018 | 589 | return (EEXIST); |
9df96926 | 590 | } |
3673d032 | 591 | } |
cd478018 | 592 | } |
593 | ||
e4f5fa12 | 594 | kmem_strfree(parent); |
cd478018 | 595 | return (0); |
596 | } | |
597 | ||
d1261452 JG |
598 | /* |
599 | * Add a file to the proc filesystem under the kstat namespace (i.e. | |
600 | * /proc/spl/kstat/). The file need not necessarily be implemented as a | |
601 | * kstat. | |
602 | */ | |
04a479f7 | 603 | void |
a887d653 | 604 | kstat_proc_entry_install(kstat_proc_entry_t *kpep, mode_t mode, |
0dd73648 | 605 | const kstat_proc_op_t *proc_ops, void *data) |
04a479f7 | 606 | { |
f2a745c4 | 607 | kstat_module_t *module; |
7cf1fe63 | 608 | kstat_proc_entry_t *tmp = NULL; |
04a479f7 | 609 | |
d1261452 | 610 | ASSERT(kpep); |
04a479f7 | 611 | |
f2a745c4 | 612 | mutex_enter(&kstat_module_lock); |
04a479f7 | 613 | |
d1261452 | 614 | module = kstat_find_module(kpep->kpe_module); |
f2a745c4 | 615 | if (module == NULL) { |
d1261452 | 616 | if (kstat_detect_collision(kpep) != 0) { |
cd478018 | 617 | cmn_err(CE_WARN, "kstat_create('%s', '%s'): namespace" \ |
d1261452 | 618 | " collision", kpep->kpe_module, kpep->kpe_name); |
cd478018 | 619 | goto out; |
620 | } | |
d1261452 | 621 | module = kstat_create_module(kpep->kpe_module); |
f2a745c4 RY |
622 | if (module == NULL) |
623 | goto out; | |
04a479f7 BB |
624 | } |
625 | ||
f2a745c4 RY |
626 | /* |
627 | * Only one entry by this name per-module, on failure the module | |
628 | * shouldn't be deleted because we know it has at least one entry. | |
629 | */ | |
d1261452 JG |
630 | list_for_each_entry(tmp, &module->ksm_kstat_list, kpe_list) { |
631 | if (strncmp(tmp->kpe_name, kpep->kpe_name, KSTAT_STRLEN) == 0) | |
f2a745c4 | 632 | goto out; |
3673d032 | 633 | } |
f2a745c4 | 634 | |
d1261452 | 635 | list_add_tail(&kpep->kpe_list, &module->ksm_kstat_list); |
04a479f7 | 636 | |
d1261452 | 637 | kpep->kpe_owner = module; |
a887d653 | 638 | kpep->kpe_proc = proc_create_data(kpep->kpe_name, mode, |
0dd73648 | 639 | module->ksm_proc, proc_ops, data); |
d1261452 JG |
640 | if (kpep->kpe_proc == NULL) { |
641 | list_del_init(&kpep->kpe_list); | |
f2a745c4 RY |
642 | if (list_empty(&module->ksm_kstat_list)) |
643 | kstat_delete_module(module); | |
644 | } | |
04a479f7 | 645 | out: |
f2a745c4 | 646 | mutex_exit(&kstat_module_lock); |
d1261452 JG |
647 | |
648 | } | |
649 | EXPORT_SYMBOL(kstat_proc_entry_install); | |
650 | ||
651 | void | |
652 | __kstat_install(kstat_t *ksp) | |
653 | { | |
654 | ASSERT(ksp); | |
a887d653 SH |
655 | mode_t mode; |
656 | /* Specify permission modes for different kstats */ | |
657 | if (strncmp(ksp->ks_proc.kpe_name, "dbufs", KSTAT_STRLEN) == 0) { | |
658 | mode = 0600; | |
659 | } else { | |
660 | mode = 0644; | |
661 | } | |
662 | kstat_proc_entry_install( | |
663 | &ksp->ks_proc, mode, &proc_kstat_operations, ksp); | |
04a479f7 BB |
664 | } |
665 | EXPORT_SYMBOL(__kstat_install); | |
666 | ||
667 | void | |
d1261452 | 668 | kstat_proc_entry_delete(kstat_proc_entry_t *kpep) |
04a479f7 | 669 | { |
d1261452 JG |
670 | kstat_module_t *module = kpep->kpe_owner; |
671 | if (kpep->kpe_proc) | |
672 | remove_proc_entry(kpep->kpe_name, module->ksm_proc); | |
04a479f7 | 673 | |
f2a745c4 | 674 | mutex_enter(&kstat_module_lock); |
d1261452 JG |
675 | list_del_init(&kpep->kpe_list); |
676 | ||
677 | /* | |
678 | * Remove top level module directory if it wasn't empty before, but now | |
679 | * is. | |
680 | */ | |
681 | if (kpep->kpe_proc && list_empty(&module->ksm_kstat_list)) | |
682 | kstat_delete_module(module); | |
f2a745c4 | 683 | mutex_exit(&kstat_module_lock); |
04a479f7 | 684 | |
d1261452 JG |
685 | } |
686 | EXPORT_SYMBOL(kstat_proc_entry_delete); | |
04a479f7 | 687 | |
d1261452 JG |
688 | void |
689 | __kstat_delete(kstat_t *ksp) | |
690 | { | |
691 | kstat_proc_entry_delete(&ksp->ks_proc); | |
04a479f7 BB |
692 | |
693 | if (!(ksp->ks_flags & KSTAT_FLAG_VIRTUAL)) | |
f2a745c4 | 694 | kmem_free(ksp->ks_data, ksp->ks_data_size); |
04a479f7 | 695 | |
ffbf0e57 CP |
696 | ksp->ks_lock = NULL; |
697 | mutex_destroy(&ksp->ks_private_lock); | |
5461eefe | 698 | kmem_free(ksp, sizeof (*ksp)); |
04a479f7 BB |
699 | } |
700 | EXPORT_SYMBOL(__kstat_delete); | |
701 | ||
04a479f7 | 702 | int |
1114ae6a | 703 | spl_kstat_init(void) |
04a479f7 | 704 | { |
f2a745c4 RY |
705 | mutex_init(&kstat_module_lock, NULL, MUTEX_DEFAULT, NULL); |
706 | INIT_LIST_HEAD(&kstat_module_list); | |
5461eefe | 707 | kstat_id = 0; |
8d9a23e8 | 708 | return (0); |
04a479f7 BB |
709 | } |
710 | ||
711 | void | |
1114ae6a | 712 | spl_kstat_fini(void) |
04a479f7 | 713 | { |
f2a745c4 RY |
714 | ASSERT(list_empty(&kstat_module_list)); |
715 | mutex_destroy(&kstat_module_lock); | |
04a479f7 | 716 | } |