]> git.proxmox.com Git - mirror_spl-debian.git/blame - modules/spl/spl-kstat.c
Add an almost feature complete implemenation of kstat. I chose
[mirror_spl-debian.git] / modules / spl / spl-kstat.c
CommitLineData
04a479f7 1#include <sys/kstat.h>
2
3#ifdef DEBUG_KSTAT
4
5static spinlock_t kstat_lock;
6static struct list_head kstat_list;
7static kid_t kstat_id;
8
9static void
10kstat_seq_show_headers(struct seq_file *f)
11{
12 kstat_t *ksp = (kstat_t *)f->private;
13 ASSERT(ksp->ks_magic == KS_MAGIC);
14
15 seq_printf(f, "%d %d 0x%02x %d %d %lld %lld\n",
16 ksp->ks_kid, ksp->ks_type, ksp->ks_flags,
17 ksp->ks_ndata, (int)ksp->ks_data_size,
18 ksp->ks_crtime, ksp->ks_snaptime);
19
20 switch (ksp->ks_type) {
21 case KSTAT_TYPE_RAW:
22 seq_printf(f, "raw data");
23 break;
24 case KSTAT_TYPE_NAMED:
25 seq_printf(f, "%-31s %-4s %s\n",
26 "name", "type", "data");
27 break;
28 case KSTAT_TYPE_INTR:
29 seq_printf(f, "%-8s %-8s %-8s %-8s %-8s\n",
30 "hard", "soft", "watchdog",
31 "spurious", "multsvc");
32 break;
33 case KSTAT_TYPE_IO:
34 seq_printf(f,
35 "%-8s %-8s %-8s %-8s %-8s %-8s "
36 "%-8s %-8s %-8s %-8s %-8s %-8s\n",
37 "nread", "nwritten", "reads", "writes",
38 "wtime", "wlentime", "wupdate",
39 "rtime", "rlentime", "rupdate",
40 "wcnt", "rcnt");
41 break;
42 case KSTAT_TYPE_TIMER:
43 seq_printf(f,
44 "%-31s %-8s "
45 "%-8s %-8s %-8s %-8s %-8s\n",
46 "name", "events", "elapsed",
47 "min", "max", "start", "stop");
48 break;
49 default:
50 SBUG(); /* Unreachable */
51 }
52}
53
54static int
55kstat_seq_show_raw(struct seq_file *f, unsigned char *p, int l)
56{
57 int i, j;
58
59 for (i = 0; ; i++) {
60 seq_printf(f, "%03x:", i);
61
62 for (j = 0; j < 16; j++) {
63 if (i * 16 + j >= l) {
64 seq_printf(f, "\n");
65 goto out;
66 }
67
68 seq_printf(f, " %02x", (unsigned char)p[i * 16 + j]);
69 }
70 seq_printf(f, "\n");
71 }
72out:
73 return 0;
74}
75
76static int
77kstat_seq_show_named(struct seq_file *f, kstat_named_t *knp)
78{
79 seq_printf(f, "%-31s %-4d ", knp->name, knp->data_type);
80
81 switch (knp->data_type) {
82 case KSTAT_DATA_CHAR:
83 knp->value.c[15] = '\0'; /* NULL terminate */
84 seq_printf(f, "%-16s", knp->value.c);
85 break;
86 /* XXX - We need to be more careful able what tokens are
87 * used for each arch, for now this is correct for x86_64.
88 */
89 case KSTAT_DATA_INT32:
90 seq_printf(f, "%d", knp->value.i32);
91 break;
92 case KSTAT_DATA_UINT32:
93 seq_printf(f, "%u", knp->value.ui32);
94 break;
95 case KSTAT_DATA_INT64:
96 seq_printf(f, "%d", (int)knp->value.i64);
97 break;
98 case KSTAT_DATA_UINT64:
99 seq_printf(f, "%u", (unsigned int)knp->value.ui64);
100 break;
101 case KSTAT_DATA_LONG:
102 seq_printf(f, "%ld", knp->value.l);
103 break;
104 case KSTAT_DATA_ULONG:
105 seq_printf(f, "%lu", knp->value.l);
106 break;
107 case KSTAT_DATA_STRING:
108 KSTAT_NAMED_STR_PTR(knp)
109 [KSTAT_NAMED_STR_BUFLEN(knp)-1] = '\0';
110 seq_printf(f, "%s", KSTAT_NAMED_STR_PTR(knp));
111 break;
112 default:
113 SBUG(); /* Unreachable */
114 }
115
116 seq_printf(f, "\n");
117
118 return 0;
119}
120
121static int
122kstat_seq_show_intr(struct seq_file *f, kstat_intr_t *kip)
123{
124 seq_printf(f, "%-8u %-8u %-8u %-8u %-8u\n",
125 kip->intrs[KSTAT_INTR_HARD],
126 kip->intrs[KSTAT_INTR_SOFT],
127 kip->intrs[KSTAT_INTR_WATCHDOG],
128 kip->intrs[KSTAT_INTR_SPURIOUS],
129 kip->intrs[KSTAT_INTR_MULTSVC]);
130
131 return 0;
132}
133
134static int
135kstat_seq_show_io(struct seq_file *f, kstat_io_t *kip)
136{
137 seq_printf(f,
138 "%-8llu %-8llu %-8u %-8u %-8lld %-8lld "
139 "%-8lld %-8lld %-8lld %-8lld %-8u %-8u\n",
140 kip->nread, kip->nwritten,
141 kip->reads, kip->writes,
142 kip->wtime, kip->wlentime, kip->wlastupdate,
143 kip->rtime, kip->wlentime, kip->rlastupdate,
144 kip->wcnt, kip->rcnt);
145
146 return 0;
147}
148
149static int
150kstat_seq_show_timer(struct seq_file *f, kstat_timer_t *ktp)
151{
152 seq_printf(f,
153 "%-31s %-8llu %-8lld %-8lld %-8lld %-8lld %-8lld\n",
154 ktp->name, ktp->num_events, ktp->elapsed_time,
155 ktp->min_time, ktp->max_time,
156 ktp->start_time, ktp->stop_time);
157
158 return 0;
159}
160
161static int
162kstat_seq_show(struct seq_file *f, void *p)
163{
164 kstat_t *ksp = (kstat_t *)f->private;
165 int rc = 0;
166
167 ASSERT(ksp->ks_magic == KS_MAGIC);
168
169 switch (ksp->ks_type) {
170 case KSTAT_TYPE_RAW:
171 ASSERT(ksp->ks_ndata == 1);
172 rc = kstat_seq_show_raw(f, ksp->ks_data,
173 ksp->ks_data_size);
174 break;
175 case KSTAT_TYPE_NAMED:
176 rc = kstat_seq_show_named(f, (kstat_named_t *)p);
177 break;
178 case KSTAT_TYPE_INTR:
179 rc = kstat_seq_show_intr(f, (kstat_intr_t *)p);
180 break;
181 case KSTAT_TYPE_IO:
182 rc = kstat_seq_show_io(f, (kstat_io_t *)p);
183 break;
184 case KSTAT_TYPE_TIMER:
185 rc = kstat_seq_show_timer(f, (kstat_timer_t *)p);
186 break;
187 default:
188 SBUG(); /* Unreachable */
189 }
190
191 return rc;
192}
193
194static void *
195kstat_seq_data_addr(kstat_t *ksp, loff_t n)
196{
197 void *rc = NULL;
198 ENTRY;
199
200 switch (ksp->ks_type) {
201 case KSTAT_TYPE_RAW:
202 rc = ksp->ks_data;
203 break;
204 case KSTAT_TYPE_NAMED:
205 rc = ksp->ks_data + n * sizeof(kstat_named_t);
206 break;
207 case KSTAT_TYPE_INTR:
208 rc = ksp->ks_data + n * sizeof(kstat_intr_t);
209 break;
210 case KSTAT_TYPE_IO:
211 rc = ksp->ks_data + n * sizeof(kstat_io_t);
212 break;
213 case KSTAT_TYPE_TIMER:
214 rc = ksp->ks_data + n * sizeof(kstat_timer_t);
215 break;
216 default:
217 SBUG(); /* Unreachable */
218 }
219
220 RETURN(rc);
221}
222
223static void *
224kstat_seq_start(struct seq_file *f, loff_t *pos)
225{
226 loff_t n = *pos;
227 kstat_t *ksp = (kstat_t *)f->private;
228 ASSERT(ksp->ks_magic == KS_MAGIC);
229 ENTRY;
230
231 spin_lock(&ksp->ks_lock);
232 ksp->ks_snaptime = gethrtime();
233
234 if (!n)
235 kstat_seq_show_headers(f);
236
237 if (n >= ksp->ks_ndata)
238 RETURN(NULL);
239
240 RETURN(kstat_seq_data_addr(ksp, n));
241}
242
243static void *
244kstat_seq_next(struct seq_file *f, void *p, loff_t *pos)
245{
246 kstat_t *ksp = (kstat_t *)f->private;
247 ASSERT(ksp->ks_magic == KS_MAGIC);
248 ENTRY;
249
250 ++*pos;
251 if (*pos >= ksp->ks_ndata)
252 RETURN(NULL);
253
254 RETURN(kstat_seq_data_addr(ksp, *pos));
255}
256
257static void
258kstat_seq_stop(struct seq_file *f, void *v)
259{
260 kstat_t *ksp = (kstat_t *)f->private;
261 ASSERT(ksp->ks_magic == KS_MAGIC);
262
263 spin_unlock(&ksp->ks_lock);
264}
265
266static struct seq_operations kstat_seq_ops = {
267 .show = kstat_seq_show,
268 .start = kstat_seq_start,
269 .next = kstat_seq_next,
270 .stop = kstat_seq_stop,
271};
272
273static int
274proc_kstat_open(struct inode *inode, struct file *filp)
275{
276 struct seq_file *f;
277 int rc;
278
279 rc = seq_open(filp, &kstat_seq_ops);
280 if (rc)
281 return rc;
282
283 f = filp->private_data;
284 f->private = PDE(inode)->data;
285
286 return rc;
287}
288
289static struct file_operations proc_kstat_operations = {
290 .open = proc_kstat_open,
291 .read = seq_read,
292 .llseek = seq_lseek,
293 .release = seq_release,
294};
295
296kstat_t *
297__kstat_create(const char *ks_module, int ks_instance, const char *ks_name,
298 const char *ks_class, uchar_t ks_type, uint_t ks_ndata,
299 uchar_t ks_flags)
300{
301 kstat_t *ksp;
302
303 ASSERT(ks_module);
304 ASSERT(ks_instance == 0);
305 ASSERT(ks_name);
306 ASSERT(!(ks_flags & KSTAT_FLAG_UNSUPPORTED));
307
308 if ((ks_type == KSTAT_TYPE_INTR) || (ks_type == KSTAT_TYPE_IO))
309 ASSERT(ks_ndata == 1);
310
311 ksp = kmem_zalloc(sizeof(*ksp), KM_SLEEP);
312 if (ksp == NULL)
313 return ksp;
314
315 spin_lock(&kstat_lock);
316 ksp->ks_kid = kstat_id;
317 kstat_id++;
318 spin_unlock(&kstat_lock);
319
320 ksp->ks_magic = KS_MAGIC;
321 spin_lock_init(&ksp->ks_lock);
322 INIT_LIST_HEAD(&ksp->ks_list);
323
324 ksp->ks_crtime = gethrtime();
325 ksp->ks_snaptime = ksp->ks_crtime;
326 strncpy(ksp->ks_module, ks_module, KSTAT_STRLEN);
327 ksp->ks_instance = ks_instance;
328 strncpy(ksp->ks_name, ks_name, KSTAT_STRLEN);
329 strncpy(ksp->ks_class, ks_class, KSTAT_STRLEN);
330 ksp->ks_type = ks_type;
331 ksp->ks_flags = ks_flags;
332
333 switch (ksp->ks_type) {
334 case KSTAT_TYPE_RAW:
335 ksp->ks_ndata = 1;
336 ksp->ks_data_size = ks_ndata;
337 break;
338 case KSTAT_TYPE_NAMED:
339 ksp->ks_ndata = ks_ndata;
340 ksp->ks_data_size = ks_ndata * sizeof(kstat_named_t);
341 break;
342 case KSTAT_TYPE_INTR:
343 ksp->ks_ndata = ks_ndata;
344 ksp->ks_data_size = ks_ndata * sizeof(kstat_intr_t);
345 break;
346 case KSTAT_TYPE_IO:
347 ksp->ks_ndata = ks_ndata;
348 ksp->ks_data_size = ks_ndata * sizeof(kstat_io_t);
349 break;
350 case KSTAT_TYPE_TIMER:
351 ksp->ks_ndata = ks_ndata;
352 ksp->ks_data_size = ks_ndata * sizeof(kstat_timer_t);
353 break;
354 default:
355 SBUG(); /* Unreachable */
356 }
357
358 if (ksp->ks_flags & KSTAT_FLAG_VIRTUAL) {
359 ksp->ks_data = NULL;
360 } else {
361 ksp->ks_data = kmem_alloc(ksp->ks_data_size, KM_SLEEP);
362 if (ksp->ks_data == NULL) {
363 kmem_free(ksp, sizeof(*ksp));
364 ksp = NULL;
365 }
366 }
367
368 return ksp;
369}
370EXPORT_SYMBOL(__kstat_create);
371
372void
373__kstat_install(kstat_t *ksp)
374{
375 struct proc_dir_entry *de_module, *de_name;
376 kstat_t *tmp;
377 int rc = 0;
378 ENTRY;
379
380 spin_lock(&kstat_lock);
381
382 /* Item may only be added to the list once */
383 list_for_each_entry(tmp, &kstat_list, ks_list) {
384 if (tmp == ksp) {
385 spin_unlock(&kstat_lock);
386 GOTO(out, rc = -EEXIST);
387 }
388 }
389
390 list_add_tail(&ksp->ks_list, &kstat_list);
391 spin_unlock(&kstat_lock);
392
393 de_module = proc_dir_entry_find(proc_sys_spl_kstat, ksp->ks_module);
394 if (de_module == NULL) {
395 de_module = proc_mkdir(ksp->ks_module, proc_sys_spl_kstat);
396 if (de_module == NULL)
397 GOTO(out, rc = -EUNATCH);
398 }
399
400 de_name = create_proc_entry(ksp->ks_name, 0444, de_module);
401 if (de_name == NULL)
402 GOTO(out, rc = -EUNATCH);
403
404 spin_lock(&ksp->ks_lock);
405 ksp->ks_proc = de_name;
406 de_name->proc_fops = &proc_kstat_operations;
407 de_name->data = (void *)ksp;
408 spin_unlock(&ksp->ks_lock);
409out:
410 if (rc) {
411 spin_lock(&kstat_lock);
412 list_del_init(&ksp->ks_list);
413 spin_unlock(&kstat_lock);
414 }
415
416 EXIT;
417}
418EXPORT_SYMBOL(__kstat_install);
419
420void
421__kstat_delete(kstat_t *ksp)
422{
423 struct proc_dir_entry *de_module;
424
425 spin_lock(&kstat_lock);
426 list_del_init(&ksp->ks_list);
427 spin_unlock(&kstat_lock);
428
429 if (ksp->ks_proc) {
430 de_module = ksp->ks_proc->parent;
431 remove_proc_entry(ksp->ks_name, de_module);
432
433 /* Remove top level module directory if it's empty */
434 if (proc_dir_entries(de_module) == 0)
435 remove_proc_entry(de_module->name, de_module->parent);
436 }
437
438 if (!(ksp->ks_flags & KSTAT_FLAG_VIRTUAL))
439 kmem_free(ksp->ks_data, ksp->ks_data_size);
440
441 kmem_free(ksp, sizeof(*ksp));
442
443 return;
444}
445EXPORT_SYMBOL(__kstat_delete);
446
447#endif /* DEBUG_KSTAT */
448
449int
450kstat_init(void)
451{
452 ENTRY;
453#ifdef DEBUG_KSTAT
454 spin_lock_init(&kstat_lock);
455 INIT_LIST_HEAD(&kstat_list);
456 kstat_id = 0;
457#endif /* DEBUG_KSTAT */
458 RETURN(0);
459}
460
461void
462kstat_fini(void)
463{
464 ENTRY;
465#ifdef DEBUG_KSTAT
466 ASSERT(list_empty(&kstat_list));
467#endif /* DEBUG_KSTAT */
468 EXIT;
469}
470