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>.
8 * This file is part of the SPL, Solaris Porting Layer.
9 * For details, see <http://zfsonlinux.org/>.
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.
16 * The SPL is distributed in the hope that it will be useful, but WITHOUT
17 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
18 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
21 * You should have received a copy of the GNU General Public License along
22 * with the SPL. If not, see <http://www.gnu.org/licenses/>.
23 *****************************************************************************
24 * Solaris Porting Layer (SPL) Proc Implementation.
25 \*****************************************************************************/
27 #include <sys/systeminfo.h>
28 #include <sys/kstat.h>
29 #include <linux/kmod.h>
30 #include <linux/seq_file.h>
31 #include <linux/proc_compat.h>
32 #include <linux/version.h>
34 #if defined(CONSTIFY_PLUGIN) && LINUX_VERSION_CODE >= KERNEL_VERSION(3,8,0)
35 typedef struct ctl_table __no_const spl_ctl_table
;
37 typedef struct ctl_table spl_ctl_table
;
41 static unsigned long table_min
= 0;
42 static unsigned long table_max
= ~0;
45 static struct ctl_table_header
*spl_header
= NULL
;
46 static struct proc_dir_entry
*proc_spl
= NULL
;
48 static struct proc_dir_entry
*proc_spl_kmem
= NULL
;
49 static struct proc_dir_entry
*proc_spl_kmem_slab
= NULL
;
50 #endif /* DEBUG_KMEM */
51 struct proc_dir_entry
*proc_spl_kstat
= NULL
;
54 proc_copyin_string(char *kbuffer
, int kbuffer_size
,
55 const char *ubuffer
, int ubuffer_size
)
59 if (ubuffer_size
> kbuffer_size
)
62 if (copy_from_user((void *)kbuffer
, (void *)ubuffer
, ubuffer_size
))
65 /* strip trailing whitespace */
66 size
= strnlen(kbuffer
, ubuffer_size
);
68 if (!isspace(kbuffer
[size
]))
75 /* no space to terminate */
76 if (size
== kbuffer_size
)
79 kbuffer
[size
+ 1] = 0;
84 proc_copyout_string(char *ubuffer
, int ubuffer_size
,
85 const char *kbuffer
, char *append
)
87 /* NB if 'append' != NULL, it's a single character to append to the
88 * copied out string - usually "\n", for /proc entries and
89 * (i.e. a terminating zero byte) for sysctl entries
91 int size
= MIN(strlen(kbuffer
), ubuffer_size
);
93 if (copy_to_user(ubuffer
, kbuffer
, size
))
96 if (append
!= NULL
&& size
< ubuffer_size
) {
97 if (copy_to_user(ubuffer
+ size
, append
, 1))
108 proc_domemused(struct ctl_table
*table
, int write
,
109 void __user
*buffer
, size_t *lenp
, loff_t
*ppos
)
112 unsigned long min
= 0, max
= ~0, val
;
113 spl_ctl_table dummy
= *table
;
116 dummy
.proc_handler
= &proc_dointvec
;
123 # ifdef HAVE_ATOMIC64_T
124 val
= atomic64_read((atomic64_t
*)table
->data
);
126 val
= atomic_read((atomic_t
*)table
->data
);
127 # endif /* HAVE_ATOMIC64_T */
128 rc
= proc_doulongvec_minmax(&dummy
, write
, buffer
, lenp
, ppos
);
135 proc_doslab(struct ctl_table
*table
, int write
,
136 void __user
*buffer
, size_t *lenp
, loff_t
*ppos
)
139 unsigned long min
= 0, max
= ~0, val
= 0, mask
;
140 spl_ctl_table dummy
= *table
;
141 spl_kmem_cache_t
*skc
;
144 dummy
.proc_handler
= &proc_dointvec
;
151 down_read(&spl_kmem_cache_sem
);
152 mask
= (unsigned long)table
->data
;
154 list_for_each_entry(skc
, &spl_kmem_cache_list
, skc_list
) {
156 /* Only use slabs of the correct kmem/vmem type */
157 if (!(skc
->skc_flags
& mask
))
160 /* Sum the specified field for selected slabs */
161 switch (mask
& (KMC_TOTAL
| KMC_ALLOC
| KMC_MAX
)) {
163 val
+= skc
->skc_slab_size
* skc
->skc_slab_total
;
166 val
+= skc
->skc_obj_size
* skc
->skc_obj_alloc
;
169 val
+= skc
->skc_obj_size
* skc
->skc_obj_max
;
174 up_read(&spl_kmem_cache_sem
);
175 rc
= proc_doulongvec_minmax(&dummy
, write
, buffer
, lenp
, ppos
);
180 #endif /* DEBUG_KMEM */
183 proc_dohostid(struct ctl_table
*table
, int write
,
184 void __user
*buffer
, size_t *lenp
, loff_t
*ppos
)
190 /* We can't use proc_doulongvec_minmax() in the write
191 * case here because hostid while a hex value has no
192 * leading 0x which confuses the helper function. */
193 rc
= proc_copyin_string(str
, sizeof(str
), buffer
, *lenp
);
197 spl_hostid
= simple_strtoul(str
, &end
, 16);
202 len
= snprintf(str
, sizeof(str
), "%lx", spl_hostid
);
206 rc
= proc_copyout_string(buffer
,*lenp
,str
+*ppos
,"\n");
219 slab_seq_show_headers(struct seq_file
*f
)
222 "--------------------- cache ----------"
223 "--------------------------------------------- "
226 "--- emergency ---\n");
229 " flags size alloc slabsize objsize "
232 "dlock alloc max\n");
236 slab_seq_show(struct seq_file
*f
, void *p
)
238 spl_kmem_cache_t
*skc
= p
;
240 ASSERT(skc
->skc_magic
== SKC_MAGIC
);
243 * Backed by Linux slab see /proc/slabinfo.
245 if (skc
->skc_flags
& KMC_SLAB
)
248 spin_lock(&skc
->skc_lock
);
249 seq_printf(f
, "%-36s ", skc
->skc_name
);
250 seq_printf(f
, "0x%05lx %9lu %9lu %8u %8u "
251 "%5lu %5lu %5lu %5lu %5lu %5lu %5lu %5lu %5lu\n",
252 (long unsigned)skc
->skc_flags
,
253 (long unsigned)(skc
->skc_slab_size
* skc
->skc_slab_total
),
254 (long unsigned)(skc
->skc_obj_size
* skc
->skc_obj_alloc
),
255 (unsigned)skc
->skc_slab_size
,
256 (unsigned)skc
->skc_obj_size
,
257 (long unsigned)skc
->skc_slab_total
,
258 (long unsigned)skc
->skc_slab_alloc
,
259 (long unsigned)skc
->skc_slab_max
,
260 (long unsigned)skc
->skc_obj_total
,
261 (long unsigned)skc
->skc_obj_alloc
,
262 (long unsigned)skc
->skc_obj_max
,
263 (long unsigned)skc
->skc_obj_deadlock
,
264 (long unsigned)skc
->skc_obj_emergency
,
265 (long unsigned)skc
->skc_obj_emergency_max
);
267 spin_unlock(&skc
->skc_lock
);
273 slab_seq_start(struct seq_file
*f
, loff_t
*pos
)
278 down_read(&spl_kmem_cache_sem
);
280 slab_seq_show_headers(f
);
282 p
= spl_kmem_cache_list
.next
;
285 if (p
== &spl_kmem_cache_list
)
289 return (list_entry(p
, spl_kmem_cache_t
, skc_list
));
293 slab_seq_next(struct seq_file
*f
, void *p
, loff_t
*pos
)
295 spl_kmem_cache_t
*skc
= p
;
298 return ((skc
->skc_list
.next
== &spl_kmem_cache_list
) ?
299 NULL
: list_entry(skc
->skc_list
.next
,spl_kmem_cache_t
,skc_list
));
303 slab_seq_stop(struct seq_file
*f
, void *v
)
305 up_read(&spl_kmem_cache_sem
);
308 static struct seq_operations slab_seq_ops
= {
309 .show
= slab_seq_show
,
310 .start
= slab_seq_start
,
311 .next
= slab_seq_next
,
312 .stop
= slab_seq_stop
,
316 proc_slab_open(struct inode
*inode
, struct file
*filp
)
318 return seq_open(filp
, &slab_seq_ops
);
321 static struct file_operations proc_slab_operations
= {
322 .open
= proc_slab_open
,
325 .release
= seq_release
,
327 #endif /* DEBUG_KMEM */
330 static struct ctl_table spl_kmem_table
[] = {
332 .procname
= "kmem_used",
333 .data
= &kmem_alloc_used
,
334 # ifdef HAVE_ATOMIC64_T
335 .maxlen
= sizeof(atomic64_t
),
337 .maxlen
= sizeof(atomic_t
),
338 # endif /* HAVE_ATOMIC64_T */
340 .proc_handler
= &proc_domemused
,
343 .procname
= "kmem_max",
344 .data
= &kmem_alloc_max
,
345 .maxlen
= sizeof(unsigned long),
346 .extra1
= &table_min
,
347 .extra2
= &table_max
,
349 .proc_handler
= &proc_doulongvec_minmax
,
352 .procname
= "vmem_used",
353 .data
= &vmem_alloc_used
,
354 # ifdef HAVE_ATOMIC64_T
355 .maxlen
= sizeof(atomic64_t
),
357 .maxlen
= sizeof(atomic_t
),
358 # endif /* HAVE_ATOMIC64_T */
360 .proc_handler
= &proc_domemused
,
363 .procname
= "vmem_max",
364 .data
= &vmem_alloc_max
,
365 .maxlen
= sizeof(unsigned long),
366 .extra1
= &table_min
,
367 .extra2
= &table_max
,
369 .proc_handler
= &proc_doulongvec_minmax
,
372 .procname
= "slab_kmem_total",
373 .data
= (void *)(KMC_KMEM
| KMC_TOTAL
),
374 .maxlen
= sizeof(unsigned long),
375 .extra1
= &table_min
,
376 .extra2
= &table_max
,
378 .proc_handler
= &proc_doslab
,
381 .procname
= "slab_kmem_alloc",
382 .data
= (void *)(KMC_KMEM
| KMC_ALLOC
),
383 .maxlen
= sizeof(unsigned long),
384 .extra1
= &table_min
,
385 .extra2
= &table_max
,
387 .proc_handler
= &proc_doslab
,
390 .procname
= "slab_kmem_max",
391 .data
= (void *)(KMC_KMEM
| KMC_MAX
),
392 .maxlen
= sizeof(unsigned long),
393 .extra1
= &table_min
,
394 .extra2
= &table_max
,
396 .proc_handler
= &proc_doslab
,
399 .procname
= "slab_vmem_total",
400 .data
= (void *)(KMC_VMEM
| KMC_TOTAL
),
401 .maxlen
= sizeof(unsigned long),
402 .extra1
= &table_min
,
403 .extra2
= &table_max
,
405 .proc_handler
= &proc_doslab
,
408 .procname
= "slab_vmem_alloc",
409 .data
= (void *)(KMC_VMEM
| KMC_ALLOC
),
410 .maxlen
= sizeof(unsigned long),
411 .extra1
= &table_min
,
412 .extra2
= &table_max
,
414 .proc_handler
= &proc_doslab
,
417 .procname
= "slab_vmem_max",
418 .data
= (void *)(KMC_VMEM
| KMC_MAX
),
419 .maxlen
= sizeof(unsigned long),
420 .extra1
= &table_min
,
421 .extra2
= &table_max
,
423 .proc_handler
= &proc_doslab
,
427 #endif /* DEBUG_KMEM */
429 static struct ctl_table spl_kstat_table
[] = {
433 static struct ctl_table spl_table
[] = {
434 /* NB No .strategy entries have been provided since
435 * sysctl(8) prefers to go via /proc for portability.
438 .procname
= "version",
440 .maxlen
= sizeof(spl_version
),
442 .proc_handler
= &proc_dostring
,
445 .procname
= "hostid",
447 .maxlen
= sizeof(unsigned long),
449 .proc_handler
= &proc_dohostid
,
455 .child
= spl_kmem_table
,
461 .child
= spl_kstat_table
,
466 static struct ctl_table spl_dir
[] = {
475 static struct ctl_table spl_root
[] = {
478 .ctl_name
= CTL_KERN
,
480 .procname
= "kernel",
492 spl_header
= register_sysctl_table(spl_root
);
493 if (spl_header
== NULL
)
496 proc_spl
= proc_mkdir("spl", NULL
);
497 if (proc_spl
== NULL
) {
503 proc_spl_kmem
= proc_mkdir("kmem", proc_spl
);
504 if (proc_spl_kmem
== NULL
) {
509 proc_spl_kmem_slab
= proc_create_data("slab", 0444,
510 proc_spl_kmem
, &proc_slab_operations
, NULL
);
511 if (proc_spl_kmem_slab
== NULL
) {
516 #endif /* DEBUG_KMEM */
518 proc_spl_kstat
= proc_mkdir("kstat", proc_spl
);
519 if (proc_spl_kstat
== NULL
) {
525 remove_proc_entry("kstat", proc_spl
);
527 remove_proc_entry("slab", proc_spl_kmem
);
528 remove_proc_entry("kmem", proc_spl
);
530 remove_proc_entry("spl", NULL
);
531 unregister_sysctl_table(spl_header
);
540 remove_proc_entry("kstat", proc_spl
);
542 remove_proc_entry("slab", proc_spl_kmem
);
543 remove_proc_entry("kmem", proc_spl
);
545 remove_proc_entry("spl", NULL
);
547 ASSERT(spl_header
!= NULL
);
548 unregister_sysctl_table(spl_header
);