]> git.proxmox.com Git - mirror_spl.git/blob - module/spl/spl-proc.c
Refactor generic memory allocation interfaces
[mirror_spl.git] / module / spl / spl-proc.c
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>.
6 * UCRL-CODE-235197
7 *
8 * This file is part of the SPL, Solaris Porting Layer.
9 * For details, see <http://zfsonlinux.org/>.
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.
15 *
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
19 * for more details.
20 *
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 \*****************************************************************************/
26
27 #include <sys/systeminfo.h>
28 #include <sys/kstat.h>
29 #include <sys/kmem.h>
30 #include <sys/kmem_cache.h>
31 #include <sys/vmem.h>
32 #include <linux/ctype.h>
33 #include <linux/kmod.h>
34 #include <linux/seq_file.h>
35 #include <linux/proc_compat.h>
36 #include <linux/uaccess.h>
37 #include <linux/version.h>
38
39 #if defined(CONSTIFY_PLUGIN) && LINUX_VERSION_CODE >= KERNEL_VERSION(3,8,0)
40 typedef struct ctl_table __no_const spl_ctl_table;
41 #else
42 typedef struct ctl_table spl_ctl_table;
43 #endif
44
45 #ifdef DEBUG_KMEM
46 static unsigned long table_min = 0;
47 static unsigned long table_max = ~0;
48 #endif
49
50 static struct ctl_table_header *spl_header = NULL;
51 static struct proc_dir_entry *proc_spl = NULL;
52 #ifdef DEBUG_KMEM
53 static struct proc_dir_entry *proc_spl_kmem = NULL;
54 static struct proc_dir_entry *proc_spl_kmem_slab = NULL;
55 #endif /* DEBUG_KMEM */
56 struct proc_dir_entry *proc_spl_kstat = NULL;
57
58 static int
59 proc_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
88 static int
89 proc_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
111 #ifdef DEBUG_KMEM
112 static int
113 proc_domemused(struct ctl_table *table, int write,
114 void __user *buffer, size_t *lenp, loff_t *ppos)
115 {
116 int rc = 0;
117 unsigned long min = 0, max = ~0, val;
118 spl_ctl_table dummy = *table;
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 {
128 # ifdef HAVE_ATOMIC64_T
129 val = atomic64_read((atomic64_t *)table->data);
130 # else
131 val = atomic_read((atomic_t *)table->data);
132 # endif /* HAVE_ATOMIC64_T */
133 rc = proc_doulongvec_minmax(&dummy, write, buffer, lenp, ppos);
134 }
135
136 return (rc);
137 }
138
139 static int
140 proc_doslab(struct ctl_table *table, int write,
141 void __user *buffer, size_t *lenp, loff_t *ppos)
142 {
143 int rc = 0;
144 unsigned long min = 0, max = ~0, val = 0, mask;
145 spl_ctl_table dummy = *table;
146 spl_kmem_cache_t *skc;
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);
180 rc = proc_doulongvec_minmax(&dummy, write, buffer, lenp, ppos);
181 }
182
183 return (rc);
184 }
185 #endif /* DEBUG_KMEM */
186
187 static int
188 proc_dohostid(struct ctl_table *table, int write,
189 void __user *buffer, size_t *lenp, loff_t *ppos)
190 {
191 int len, rc = 0;
192 char *end, str[32];
193
194 if (write) {
195 /* We can't use proc_doulongvec_minmax() in the write
196 * case here because hostid while a hex value has no
197 * leading 0x which confuses the helper function. */
198 rc = proc_copyin_string(str, sizeof(str), buffer, *lenp);
199 if (rc < 0)
200 return (rc);
201
202 spl_hostid = simple_strtoul(str, &end, 16);
203 if (str == end)
204 return (-EINVAL);
205
206 } else {
207 len = snprintf(str, sizeof(str), "%lx", spl_hostid);
208 if (*ppos >= len)
209 rc = 0;
210 else
211 rc = proc_copyout_string(buffer,*lenp,str+*ppos,"\n");
212
213 if (rc >= 0) {
214 *lenp = rc;
215 *ppos += rc;
216 }
217 }
218
219 return (rc);
220 }
221
222 #ifdef DEBUG_KMEM
223 static void
224 slab_seq_show_headers(struct seq_file *f)
225 {
226 seq_printf(f,
227 "--------------------- cache ----------"
228 "--------------------------------------------- "
229 "----- slab ------ "
230 "---- object ----- "
231 "--- emergency ---\n");
232 seq_printf(f,
233 "name "
234 " flags size alloc slabsize objsize "
235 "total alloc max "
236 "total alloc max "
237 "dlock alloc max\n");
238 }
239
240 static int
241 slab_seq_show(struct seq_file *f, void *p)
242 {
243 spl_kmem_cache_t *skc = p;
244
245 ASSERT(skc->skc_magic == SKC_MAGIC);
246
247 /*
248 * Backed by Linux slab see /proc/slabinfo.
249 */
250 if (skc->skc_flags & KMC_SLAB)
251 return (0);
252
253 spin_lock(&skc->skc_lock);
254 seq_printf(f, "%-36s ", skc->skc_name);
255 seq_printf(f, "0x%05lx %9lu %9lu %8u %8u "
256 "%5lu %5lu %5lu %5lu %5lu %5lu %5lu %5lu %5lu\n",
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,
267 (long unsigned)skc->skc_obj_max,
268 (long unsigned)skc->skc_obj_deadlock,
269 (long unsigned)skc->skc_obj_emergency,
270 (long unsigned)skc->skc_obj_emergency_max);
271
272 spin_unlock(&skc->skc_lock);
273
274 return 0;
275 }
276
277 static void *
278 slab_seq_start(struct seq_file *f, loff_t *pos)
279 {
280 struct list_head *p;
281 loff_t n = *pos;
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)
291 return (NULL);
292 }
293
294 return (list_entry(p, spl_kmem_cache_t, skc_list));
295 }
296
297 static void *
298 slab_seq_next(struct seq_file *f, void *p, loff_t *pos)
299 {
300 spl_kmem_cache_t *skc = p;
301
302 ++*pos;
303 return ((skc->skc_list.next == &spl_kmem_cache_list) ?
304 NULL : list_entry(skc->skc_list.next,spl_kmem_cache_t,skc_list));
305 }
306
307 static void
308 slab_seq_stop(struct seq_file *f, void *v)
309 {
310 up_read(&spl_kmem_cache_sem);
311 }
312
313 static 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
320 static int
321 proc_slab_open(struct inode *inode, struct file *filp)
322 {
323 return seq_open(filp, &slab_seq_ops);
324 }
325
326 static 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
334 #ifdef DEBUG_KMEM
335 static struct ctl_table spl_kmem_table[] = {
336 {
337 .procname = "kmem_used",
338 .data = &kmem_alloc_used,
339 # ifdef HAVE_ATOMIC64_T
340 .maxlen = sizeof(atomic64_t),
341 # else
342 .maxlen = sizeof(atomic_t),
343 # endif /* HAVE_ATOMIC64_T */
344 .mode = 0444,
345 .proc_handler = &proc_domemused,
346 },
347 {
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 {
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 {
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 {
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 {
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 {
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 {
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 },
410 {0},
411 };
412 #endif /* DEBUG_KMEM */
413
414 static struct ctl_table spl_kstat_table[] = {
415 {0},
416 };
417
418 static 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 {
423 .procname = "version",
424 .data = spl_version,
425 .maxlen = sizeof(spl_version),
426 .mode = 0444,
427 .proc_handler = &proc_dostring,
428 },
429 {
430 .procname = "hostid",
431 .data = &spl_hostid,
432 .maxlen = sizeof(unsigned long),
433 .mode = 0644,
434 .proc_handler = &proc_dohostid,
435 },
436 #ifdef DEBUG_KMEM
437 {
438 .procname = "kmem",
439 .mode = 0555,
440 .child = spl_kmem_table,
441 },
442 #endif
443 {
444 .procname = "kstat",
445 .mode = 0555,
446 .child = spl_kstat_table,
447 },
448 { 0 },
449 };
450
451 static struct ctl_table spl_dir[] = {
452 {
453 .procname = "spl",
454 .mode = 0555,
455 .child = spl_table,
456 },
457 { 0 }
458 };
459
460 static struct ctl_table spl_root[] = {
461 {
462 #ifdef HAVE_CTL_NAME
463 .ctl_name = CTL_KERN,
464 #endif
465 .procname = "kernel",
466 .mode = 0555,
467 .child = spl_dir,
468 },
469 { 0 }
470 };
471
472 int
473 spl_proc_init(void)
474 {
475 int rc = 0;
476
477 spl_header = register_sysctl_table(spl_root);
478 if (spl_header == NULL)
479 return (-EUNATCH);
480
481 proc_spl = proc_mkdir("spl", NULL);
482 if (proc_spl == NULL) {
483 rc = -EUNATCH;
484 goto out;
485 }
486
487 #ifdef DEBUG_KMEM
488 proc_spl_kmem = proc_mkdir("kmem", proc_spl);
489 if (proc_spl_kmem == NULL) {
490 rc = -EUNATCH;
491 goto out;
492 }
493
494 proc_spl_kmem_slab = proc_create_data("slab", 0444,
495 proc_spl_kmem, &proc_slab_operations, NULL);
496 if (proc_spl_kmem_slab == NULL) {
497 rc = -EUNATCH;
498 goto out;
499 }
500
501 #endif /* DEBUG_KMEM */
502
503 proc_spl_kstat = proc_mkdir("kstat", proc_spl);
504 if (proc_spl_kstat == NULL) {
505 rc = -EUNATCH;
506 goto out;
507 }
508 out:
509 if (rc) {
510 remove_proc_entry("kstat", proc_spl);
511 #ifdef DEBUG_KMEM
512 remove_proc_entry("slab", proc_spl_kmem);
513 remove_proc_entry("kmem", proc_spl);
514 #endif
515 remove_proc_entry("spl", NULL);
516 unregister_sysctl_table(spl_header);
517 }
518
519 return (rc);
520 }
521
522 void
523 spl_proc_fini(void)
524 {
525 remove_proc_entry("kstat", proc_spl);
526 #ifdef DEBUG_KMEM
527 remove_proc_entry("slab", proc_spl_kmem);
528 remove_proc_entry("kmem", proc_spl);
529 #endif
530 remove_proc_entry("spl", NULL);
531
532 ASSERT(spl_header != NULL);
533 unregister_sysctl_table(spl_header);
534 }