]> git.proxmox.com Git - mirror_spl-debian.git/blame - module/spl/spl-proc.c
Imported Upstream version 0.6.4.1
[mirror_spl-debian.git] / module / spl / spl-proc.c
CommitLineData
716154c5
BB
1/*****************************************************************************\
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 6 * UCRL-CODE-235197
7 *
716154c5 8 * This file is part of the SPL, Solaris Porting Layer.
3d6af2dd 9 * For details, see <http://zfsonlinux.org/>.
716154c5
BB
10 *
11 * The SPL is free software; you can redistribute it and/or modify it
12 * under the terms of the GNU General Public License as published by the
13 * Free Software Foundation; either version 2 of the License, or (at your
14 * option) any later version.
715f6251 15 *
716154c5 16 * The SPL is distributed in the hope that it will be useful, but WITHOUT
715f6251 17 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
18 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
19 * for more details.
20 *
21 * You should have received a copy of the GNU General Public License along
716154c5
BB
22 * with the SPL. If not, see <http://www.gnu.org/licenses/>.
23 *****************************************************************************
24 * Solaris Porting Layer (SPL) Proc Implementation.
25\*****************************************************************************/
715f6251 26
ae4c36ad
BB
27#include <sys/systeminfo.h>
28#include <sys/kstat.h>
10946b02
AX
29#include <sys/kmem.h>
30#include <sys/kmem_cache.h>
31#include <sys/vmem.h>
32#include <linux/ctype.h>
ae4c36ad
BB
33#include <linux/kmod.h>
34#include <linux/seq_file.h>
35#include <linux/proc_compat.h>
10946b02 36#include <linux/uaccess.h>
80093b6f 37#include <linux/version.h>
57d1b188 38
80093b6f
AX
39#if defined(CONSTIFY_PLUGIN) && LINUX_VERSION_CODE >= KERNEL_VERSION(3,8,0)
40typedef struct ctl_table __no_const spl_ctl_table;
41#else
42typedef struct ctl_table spl_ctl_table;
43#endif
44
404992e3 45#ifdef DEBUG_KMEM
57d1b188 46static unsigned long table_min = 0;
47static unsigned long table_max = ~0;
404992e3 48#endif
49
404992e3 50static struct ctl_table_header *spl_header = NULL;
c30df9c8 51static struct proc_dir_entry *proc_spl = NULL;
04a479f7 52#ifdef DEBUG_KMEM
c30df9c8 53static struct proc_dir_entry *proc_spl_kmem = NULL;
ff449ac4 54static struct proc_dir_entry *proc_spl_kmem_slab = NULL;
c30df9c8 55#endif /* DEBUG_KMEM */
c30df9c8 56struct proc_dir_entry *proc_spl_kstat = NULL;
57d1b188 57
57d1b188 58static int
59proc_copyin_string(char *kbuffer, int kbuffer_size,
60 const char *ubuffer, int ubuffer_size)
61{
62 int size;
63
64 if (ubuffer_size > kbuffer_size)
65 return -EOVERFLOW;
66
67 if (copy_from_user((void *)kbuffer, (void *)ubuffer, ubuffer_size))
68 return -EFAULT;
69
70 /* strip trailing whitespace */
71 size = strnlen(kbuffer, ubuffer_size);
72 while (size-- >= 0)
73 if (!isspace(kbuffer[size]))
74 break;
75
76 /* empty string */
77 if (size < 0)
78 return -EINVAL;
79
80 /* no space to terminate */
81 if (size == kbuffer_size)
82 return -EOVERFLOW;
83
84 kbuffer[size + 1] = 0;
85 return 0;
86}
87
88static int
89proc_copyout_string(char *ubuffer, int ubuffer_size,
90 const char *kbuffer, char *append)
91{
92 /* NB if 'append' != NULL, it's a single character to append to the
93 * copied out string - usually "\n", for /proc entries and
94 * (i.e. a terminating zero byte) for sysctl entries
95 */
96 int size = MIN(strlen(kbuffer), ubuffer_size);
97
98 if (copy_to_user(ubuffer, kbuffer, size))
99 return -EFAULT;
100
101 if (append != NULL && size < ubuffer_size) {
102 if (copy_to_user(ubuffer + size, append, 1))
103 return -EFAULT;
104
105 size++;
106 }
107
108 return size;
109}
110
c6dc93d6 111#ifdef DEBUG_KMEM
10946b02
AX
112static int
113proc_domemused(struct ctl_table *table, int write,
114 void __user *buffer, size_t *lenp, loff_t *ppos)
57d1b188 115{
116 int rc = 0;
117 unsigned long min = 0, max = ~0, val;
80093b6f 118 spl_ctl_table dummy = *table;
57d1b188 119
120 dummy.data = &val;
121 dummy.proc_handler = &proc_dointvec;
122 dummy.extra1 = &min;
123 dummy.extra2 = &max;
124
125 if (write) {
126 *ppos += *lenp;
127 } else {
d04c8a56 128# ifdef HAVE_ATOMIC64_T
550f1705 129 val = atomic64_read((atomic64_t *)table->data);
d04c8a56
BB
130# else
131 val = atomic_read((atomic_t *)table->data);
132# endif /* HAVE_ATOMIC64_T */
10946b02 133 rc = proc_doulongvec_minmax(&dummy, write, buffer, lenp, ppos);
57d1b188 134 }
135
10946b02 136 return (rc);
57d1b188 137}
3336e29c 138
10946b02
AX
139static int
140proc_doslab(struct ctl_table *table, int write,
141 void __user *buffer, size_t *lenp, loff_t *ppos)
3336e29c
BB
142{
143 int rc = 0;
144 unsigned long min = 0, max = ~0, val = 0, mask;
80093b6f 145 spl_ctl_table dummy = *table;
3336e29c 146 spl_kmem_cache_t *skc;
3336e29c
BB
147
148 dummy.data = &val;
149 dummy.proc_handler = &proc_dointvec;
150 dummy.extra1 = &min;
151 dummy.extra2 = &max;
152
153 if (write) {
154 *ppos += *lenp;
155 } else {
156 down_read(&spl_kmem_cache_sem);
157 mask = (unsigned long)table->data;
158
159 list_for_each_entry(skc, &spl_kmem_cache_list, skc_list) {
160
161 /* Only use slabs of the correct kmem/vmem type */
162 if (!(skc->skc_flags & mask))
163 continue;
164
165 /* Sum the specified field for selected slabs */
166 switch (mask & (KMC_TOTAL | KMC_ALLOC | KMC_MAX)) {
167 case KMC_TOTAL:
168 val += skc->skc_slab_size * skc->skc_slab_total;
169 break;
170 case KMC_ALLOC:
171 val += skc->skc_obj_size * skc->skc_obj_alloc;
172 break;
173 case KMC_MAX:
174 val += skc->skc_obj_size * skc->skc_obj_max;
175 break;
176 }
177 }
178
179 up_read(&spl_kmem_cache_sem);
10946b02 180 rc = proc_doulongvec_minmax(&dummy, write, buffer, lenp, ppos);
3336e29c
BB
181 }
182
10946b02 183 return (rc);
3336e29c 184}
c6dc93d6 185#endif /* DEBUG_KMEM */
57d1b188 186
10946b02
AX
187static int
188proc_dohostid(struct ctl_table *table, int write,
189 void __user *buffer, size_t *lenp, loff_t *ppos)
57d1b188 190{
191 int len, rc = 0;
57d1b188 192 char *end, str[32];
57d1b188 193
194 if (write) {
10946b02 195 /* We can't use proc_doulongvec_minmax() in the write
c95b308d 196 * case here because hostid while a hex value has no
a0b5ae8a 197 * leading 0x which confuses the helper function. */
57d1b188 198 rc = proc_copyin_string(str, sizeof(str), buffer, *lenp);
199 if (rc < 0)
10946b02 200 return (rc);
57d1b188 201
fa6f7d8f 202 spl_hostid = simple_strtoul(str, &end, 16);
a0b5ae8a 203 if (str == end)
10946b02 204 return (-EINVAL);
57d1b188 205
57d1b188 206 } else {
c95b308d 207 len = snprintf(str, sizeof(str), "%lx", spl_hostid);
57d1b188 208 if (*ppos >= len)
209 rc = 0;
210 else
3977f837 211 rc = proc_copyout_string(buffer,*lenp,str+*ppos,"\n");
57d1b188 212
213 if (rc >= 0) {
214 *lenp = rc;
215 *ppos += rc;
216 }
217 }
218
10946b02 219 return (rc);
4ab13d3b
BB
220}
221
ff449ac4 222#ifdef DEBUG_KMEM
223static void
224slab_seq_show_headers(struct seq_file *f)
225{
d0a1038f
BB
226 seq_printf(f,
227 "--------------------- cache ----------"
228 "--------------------------------------------- "
229 "----- slab ------ "
165f13c3
BB
230 "---- object ----- "
231 "--- emergency ---\n");
d0a1038f
BB
232 seq_printf(f,
233 "name "
234 " flags size alloc slabsize objsize "
235 "total alloc max "
165f13c3
BB
236 "total alloc max "
237 "dlock alloc max\n");
ff449ac4 238}
239
240static int
241slab_seq_show(struct seq_file *f, void *p)
242{
242f539a 243 spl_kmem_cache_t *skc = p;
ff449ac4 244
242f539a 245 ASSERT(skc->skc_magic == SKC_MAGIC);
ff449ac4 246
9e4fb5c2
LG
247 /*
248 * Backed by Linux slab see /proc/slabinfo.
249 */
250 if (skc->skc_flags & KMC_SLAB)
251 return (0);
252
242f539a 253 spin_lock(&skc->skc_lock);
d0a1038f
BB
254 seq_printf(f, "%-36s ", skc->skc_name);
255 seq_printf(f, "0x%05lx %9lu %9lu %8u %8u "
165f13c3 256 "%5lu %5lu %5lu %5lu %5lu %5lu %5lu %5lu %5lu\n",
d0a1038f
BB
257 (long unsigned)skc->skc_flags,
258 (long unsigned)(skc->skc_slab_size * skc->skc_slab_total),
259 (long unsigned)(skc->skc_obj_size * skc->skc_obj_alloc),
260 (unsigned)skc->skc_slab_size,
261 (unsigned)skc->skc_obj_size,
262 (long unsigned)skc->skc_slab_total,
263 (long unsigned)skc->skc_slab_alloc,
264 (long unsigned)skc->skc_slab_max,
265 (long unsigned)skc->skc_obj_total,
266 (long unsigned)skc->skc_obj_alloc,
e2dcc6e2 267 (long unsigned)skc->skc_obj_max,
165f13c3 268 (long unsigned)skc->skc_obj_deadlock,
e2dcc6e2
BB
269 (long unsigned)skc->skc_obj_emergency,
270 (long unsigned)skc->skc_obj_emergency_max);
242f539a
BB
271
272 spin_unlock(&skc->skc_lock);
ff449ac4 273
274 return 0;
275}
276
277static void *
278slab_seq_start(struct seq_file *f, loff_t *pos)
279{
280 struct list_head *p;
281 loff_t n = *pos;
ff449ac4 282
283 down_read(&spl_kmem_cache_sem);
284 if (!n)
285 slab_seq_show_headers(f);
286
287 p = spl_kmem_cache_list.next;
288 while (n--) {
289 p = p->next;
290 if (p == &spl_kmem_cache_list)
10946b02 291 return (NULL);
ff449ac4 292 }
293
10946b02 294 return (list_entry(p, spl_kmem_cache_t, skc_list));
ff449ac4 295}
296
297static void *
298slab_seq_next(struct seq_file *f, void *p, loff_t *pos)
299{
300 spl_kmem_cache_t *skc = p;
ff449ac4 301
302 ++*pos;
10946b02 303 return ((skc->skc_list.next == &spl_kmem_cache_list) ?
3977f837 304 NULL : list_entry(skc->skc_list.next,spl_kmem_cache_t,skc_list));
ff449ac4 305}
306
307static void
308slab_seq_stop(struct seq_file *f, void *v)
309{
310 up_read(&spl_kmem_cache_sem);
311}
312
313static struct seq_operations slab_seq_ops = {
314 .show = slab_seq_show,
315 .start = slab_seq_start,
316 .next = slab_seq_next,
317 .stop = slab_seq_stop,
318};
319
320static int
321proc_slab_open(struct inode *inode, struct file *filp)
322{
323 return seq_open(filp, &slab_seq_ops);
324}
325
326static struct file_operations proc_slab_operations = {
327 .open = proc_slab_open,
328 .read = seq_read,
329 .llseek = seq_lseek,
330 .release = seq_release,
331};
332#endif /* DEBUG_KMEM */
333
57d1b188 334#ifdef DEBUG_KMEM
9ab1ac14 335static struct ctl_table spl_kmem_table[] = {
57d1b188 336 {
57d1b188 337 .procname = "kmem_used",
338 .data = &kmem_alloc_used,
d04c8a56 339# ifdef HAVE_ATOMIC64_T
57d1b188 340 .maxlen = sizeof(atomic64_t),
d04c8a56
BB
341# else
342 .maxlen = sizeof(atomic_t),
343# endif /* HAVE_ATOMIC64_T */
57d1b188 344 .mode = 0444,
d04c8a56 345 .proc_handler = &proc_domemused,
57d1b188 346 },
347 {
57d1b188 348 .procname = "kmem_max",
349 .data = &kmem_alloc_max,
350 .maxlen = sizeof(unsigned long),
351 .extra1 = &table_min,
352 .extra2 = &table_max,
353 .mode = 0444,
354 .proc_handler = &proc_doulongvec_minmax,
355 },
356 {
3336e29c
BB
357 .procname = "slab_kmem_total",
358 .data = (void *)(KMC_KMEM | KMC_TOTAL),
359 .maxlen = sizeof(unsigned long),
360 .extra1 = &table_min,
361 .extra2 = &table_max,
362 .mode = 0444,
363 .proc_handler = &proc_doslab,
364 },
365 {
3336e29c
BB
366 .procname = "slab_kmem_alloc",
367 .data = (void *)(KMC_KMEM | KMC_ALLOC),
368 .maxlen = sizeof(unsigned long),
369 .extra1 = &table_min,
370 .extra2 = &table_max,
371 .mode = 0444,
372 .proc_handler = &proc_doslab,
373 },
374 {
3336e29c
BB
375 .procname = "slab_kmem_max",
376 .data = (void *)(KMC_KMEM | KMC_MAX),
377 .maxlen = sizeof(unsigned long),
378 .extra1 = &table_min,
379 .extra2 = &table_max,
380 .mode = 0444,
381 .proc_handler = &proc_doslab,
382 },
383 {
3336e29c
BB
384 .procname = "slab_vmem_total",
385 .data = (void *)(KMC_VMEM | KMC_TOTAL),
386 .maxlen = sizeof(unsigned long),
387 .extra1 = &table_min,
388 .extra2 = &table_max,
389 .mode = 0444,
390 .proc_handler = &proc_doslab,
391 },
392 {
3336e29c
BB
393 .procname = "slab_vmem_alloc",
394 .data = (void *)(KMC_VMEM | KMC_ALLOC),
395 .maxlen = sizeof(unsigned long),
396 .extra1 = &table_min,
397 .extra2 = &table_max,
398 .mode = 0444,
399 .proc_handler = &proc_doslab,
400 },
401 {
3336e29c
BB
402 .procname = "slab_vmem_max",
403 .data = (void *)(KMC_VMEM | KMC_MAX),
404 .maxlen = sizeof(unsigned long),
405 .extra1 = &table_min,
406 .extra2 = &table_max,
407 .mode = 0444,
408 .proc_handler = &proc_doslab,
409 },
9ab1ac14 410 {0},
411};
04a479f7 412#endif /* DEBUG_KMEM */
413
04a479f7 414static struct ctl_table spl_kstat_table[] = {
415 {0},
416};
9ab1ac14 417
418static struct ctl_table spl_table[] = {
419 /* NB No .strategy entries have been provided since
420 * sysctl(8) prefers to go via /proc for portability.
421 */
422 {
9ab1ac14 423 .procname = "version",
424 .data = spl_version,
425 .maxlen = sizeof(spl_version),
426 .mode = 0444,
427 .proc_handler = &proc_dostring,
428 },
57d1b188 429 {
57d1b188 430 .procname = "hostid",
431 .data = &spl_hostid,
432 .maxlen = sizeof(unsigned long),
433 .mode = 0644,
434 .proc_handler = &proc_dohostid,
435 },
9ab1ac14 436#ifdef DEBUG_KMEM
437 {
9ab1ac14 438 .procname = "kmem",
439 .mode = 0555,
440 .child = spl_kmem_table,
441 },
04a479f7 442#endif
04a479f7 443 {
04a479f7 444 .procname = "kstat",
445 .mode = 0555,
446 .child = spl_kstat_table,
447 },
57d1b188 448 { 0 },
449};
450
9ab1ac14 451static struct ctl_table spl_dir[] = {
57d1b188 452 {
57d1b188 453 .procname = "spl",
454 .mode = 0555,
455 .child = spl_table,
456 },
57d86234 457 { 0 }
458};
459
460static struct ctl_table spl_root[] = {
461 {
10946b02
AX
462#ifdef HAVE_CTL_NAME
463 .ctl_name = CTL_KERN,
464#endif
57d86234 465 .procname = "kernel",
466 .mode = 0555,
467 .child = spl_dir,
468 },
469 { 0 }
57d1b188 470};
471
472int
1114ae6a 473spl_proc_init(void)
57d1b188 474{
404992e3 475 int rc = 0;
57d1b188 476
10946b02 477 spl_header = register_sysctl_table(spl_root);
57d1b188 478 if (spl_header == NULL)
10946b02 479 return (-EUNATCH);
9ab1ac14 480
c30df9c8 481 proc_spl = proc_mkdir("spl", NULL);
10946b02
AX
482 if (proc_spl == NULL) {
483 rc = -EUNATCH;
484 goto out;
485 }
404992e3 486
04a479f7 487#ifdef DEBUG_KMEM
c30df9c8 488 proc_spl_kmem = proc_mkdir("kmem", proc_spl);
10946b02
AX
489 if (proc_spl_kmem == NULL) {
490 rc = -EUNATCH;
491 goto out;
492 }
ff449ac4 493
80093b6f
AX
494 proc_spl_kmem_slab = proc_create_data("slab", 0444,
495 proc_spl_kmem, &proc_slab_operations, NULL);
10946b02
AX
496 if (proc_spl_kmem_slab == NULL) {
497 rc = -EUNATCH;
498 goto out;
499 }
ff449ac4 500
04a479f7 501#endif /* DEBUG_KMEM */
502
c30df9c8 503 proc_spl_kstat = proc_mkdir("kstat", proc_spl);
10946b02
AX
504 if (proc_spl_kstat == NULL) {
505 rc = -EUNATCH;
506 goto out;
507 }
404992e3 508out:
c30df9c8 509 if (rc) {
510 remove_proc_entry("kstat", proc_spl);
ff449ac4 511#ifdef DEBUG_KMEM
512 remove_proc_entry("slab", proc_spl_kmem);
c30df9c8 513 remove_proc_entry("kmem", proc_spl);
055ffd98 514#endif
a02118a8 515 remove_proc_entry("spl", NULL);
10946b02 516 unregister_sysctl_table(spl_header);
c30df9c8 517 }
c30df9c8 518
10946b02 519 return (rc);
57d1b188 520}
521
522void
1114ae6a 523spl_proc_fini(void)
57d1b188 524{
c30df9c8 525 remove_proc_entry("kstat", proc_spl);
ff449ac4 526#ifdef DEBUG_KMEM
527 remove_proc_entry("slab", proc_spl_kmem);
c30df9c8 528 remove_proc_entry("kmem", proc_spl);
055ffd98 529#endif
a02118a8 530 remove_proc_entry("spl", NULL);
c30df9c8 531
57d1b188 532 ASSERT(spl_header != NULL);
10946b02 533 unregister_sysctl_table(spl_header);
57d1b188 534}