4 * Copyright (C) 2001-2002 Sistina Software
6 * January-May,December 2001
9 * LVM driver is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2, or (at your option)
14 * LVM driver is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with GNU CC; see the file COPYING. If not, write to
21 * the Free Software Foundation, 59 Temple Place - Suite 330,
22 * Boston, MA 02111-1307, USA.
29 * 11/01/2001 - First version (Joe Thornber)
30 * 21/03/2001 - added display of stripes and stripe size (HM)
31 * 04/10/2001 - corrected devfs_register() call in lvm_init_fs()
32 * 11/04/2001 - don't devfs_register("lvm") as user-space always does it
33 * 10/05/2001 - show more of PV name in /proc/lvm/global
34 * 16/12/2001 - fix devfs unregister order and prevent duplicate unreg (REG)
38 #include <linux/config.h>
39 #include <linux/version.h>
41 #include <linux/kernel.h>
42 #include <linux/vmalloc.h>
43 #include <linux/smp_lock.h>
45 #include <linux/devfs_fs_kernel.h>
46 #include <linux/proc_fs.h>
47 #include <linux/init.h>
48 #include <linux/lvm.h>
50 #include "lvm-internal.h"
53 static int _proc_read_vg(char *page, char **start, off_t off,
54 int count, int *eof, void *data);
55 static int _proc_read_lv(char *page, char **start, off_t off,
56 int count, int *eof, void *data);
57 static int _proc_read_pv(char *page, char **start, off_t off,
58 int count, int *eof, void *data);
59 static int _proc_read_global(char *page, char **start, off_t off,
60 int count, int *eof, void *data);
62 static int _vg_info(vg_t * vg_ptr, char *buf);
63 static int _lv_info(vg_t * vg_ptr, lv_t * lv_ptr, char *buf);
64 static int _pv_info(pv_t * pv_ptr, char *buf);
66 static void _show_uuid(const char *src, char *b, char *e);
69 static devfs_handle_t lvm_devfs_handle;
71 static devfs_handle_t vg_devfs_handle[MAX_VG];
72 static devfs_handle_t ch_devfs_handle[MAX_VG];
73 static devfs_handle_t lv_devfs_handle[MAX_LV];
75 static struct proc_dir_entry *lvm_proc_dir = NULL;
76 static struct proc_dir_entry *lvm_proc_vg_subdir = NULL;
78 /* inline functions */
80 /* public interface */
81 void __init lvm_init_fs()
83 struct proc_dir_entry *pde;
85 /* User-space has already registered this */
87 lvm_devfs_handle = devfs_register(0, "lvm", 0, LVM_CHAR_MAJOR, 0,
88 S_IFCHR | S_IRUSR | S_IWUSR |
89 S_IRGRP, &lvm_chr_fops, NULL);
91 lvm_proc_dir = create_proc_entry(LVM_DIR, S_IFDIR, &proc_root);
94 create_proc_entry(LVM_VG_SUBDIR, S_IFDIR,
96 pde = create_proc_entry(LVM_GLOBAL, S_IFREG, lvm_proc_dir);
98 pde->read_proc = _proc_read_global;
105 devfs_unregister(lvm_devfs_handle);
107 remove_proc_entry(LVM_GLOBAL, lvm_proc_dir);
108 remove_proc_entry(LVM_VG_SUBDIR, lvm_proc_dir);
109 remove_proc_entry(LVM_DIR, &proc_root);
112 void lvm_fs_create_vg(vg_t * vg_ptr)
114 struct proc_dir_entry *pde;
119 vg_devfs_handle[vg_ptr->vg_number] =
120 devfs_mk_dir(0, vg_ptr->vg_name, NULL);
122 ch_devfs_handle[vg_ptr->vg_number] =
123 devfs_register(vg_devfs_handle[vg_ptr->vg_number], "group",
124 DEVFS_FL_DEFAULT, LVM_CHAR_MAJOR,
126 S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP,
127 &lvm_chr_fops, NULL);
129 vg_ptr->vg_dir_pde = create_proc_entry(vg_ptr->vg_name, S_IFDIR,
133 create_proc_entry("group", S_IFREG, vg_ptr->vg_dir_pde))) {
134 pde->read_proc = _proc_read_vg;
138 vg_ptr->lv_subdir_pde =
139 create_proc_entry(LVM_LV_SUBDIR, S_IFDIR, vg_ptr->vg_dir_pde);
141 vg_ptr->pv_subdir_pde =
142 create_proc_entry(LVM_PV_SUBDIR, S_IFDIR, vg_ptr->vg_dir_pde);
145 void lvm_fs_remove_vg(vg_t * vg_ptr)
152 devfs_unregister(ch_devfs_handle[vg_ptr->vg_number]);
153 ch_devfs_handle[vg_ptr->vg_number] = NULL;
156 for (i = 0; i < vg_ptr->lv_max; i++)
158 lvm_fs_remove_lv(vg_ptr, vg_ptr->lv[i]);
160 /* must not remove directory before leaf nodes */
161 devfs_unregister(vg_devfs_handle[vg_ptr->vg_number]);
162 vg_devfs_handle[vg_ptr->vg_number] = NULL;
165 for (i = 0; i < vg_ptr->pv_max; i++)
167 lvm_fs_remove_pv(vg_ptr, vg_ptr->pv[i]);
169 if (vg_ptr->vg_dir_pde) {
170 remove_proc_entry(LVM_LV_SUBDIR, vg_ptr->vg_dir_pde);
171 vg_ptr->lv_subdir_pde = NULL;
173 remove_proc_entry(LVM_PV_SUBDIR, vg_ptr->vg_dir_pde);
174 vg_ptr->pv_subdir_pde = NULL;
176 remove_proc_entry("group", vg_ptr->vg_dir_pde);
177 vg_ptr->vg_dir_pde = NULL;
179 remove_proc_entry(vg_ptr->vg_name, lvm_proc_vg_subdir);
184 static inline const char *_basename(const char *str)
186 const char *name = strrchr(str, '/');
187 name = name ? name + 1 : str;
191 devfs_handle_t lvm_fs_create_lv(vg_t * vg_ptr, lv_t * lv)
193 struct proc_dir_entry *pde;
199 name = _basename(lv->lv_name);
201 lv_devfs_handle[MINOR(lv->lv_dev)] =
202 devfs_register(vg_devfs_handle[vg_ptr->vg_number], name,
203 DEVFS_FL_DEFAULT, LVM_BLK_MAJOR,
205 S_IFBLK | S_IRUSR | S_IWUSR | S_IRGRP,
206 &lvm_blk_dops, NULL);
208 if (vg_ptr->lv_subdir_pde &&
210 create_proc_entry(name, S_IFREG, vg_ptr->lv_subdir_pde))) {
211 pde->read_proc = _proc_read_lv;
214 return lv_devfs_handle[MINOR(lv->lv_dev)];
217 void lvm_fs_remove_lv(vg_t * vg_ptr, lv_t * lv)
223 devfs_unregister(lv_devfs_handle[MINOR(lv->lv_dev)]);
224 lv_devfs_handle[MINOR(lv->lv_dev)] = NULL;
226 if (vg_ptr->lv_subdir_pde) {
227 const char *name = _basename(lv->lv_name);
228 remove_proc_entry(name, vg_ptr->lv_subdir_pde);
233 static inline void _make_pv_name(const char *src, char *b, char *e)
235 int offset = strlen(LVM_DIR_PREFIX);
236 if (strncmp(src, LVM_DIR_PREFIX, offset))
241 while (*src && (b != e)) {
242 *b++ = (*src == '/') ? '_' : *src;
248 void lvm_fs_create_pv(vg_t * vg_ptr, pv_t * pv)
250 struct proc_dir_entry *pde;
256 if (!vg_ptr->pv_subdir_pde)
259 _make_pv_name(pv->pv_name, name, name + sizeof(name));
261 create_proc_entry(name, S_IFREG, vg_ptr->pv_subdir_pde))) {
262 pde->read_proc = _proc_read_pv;
267 void lvm_fs_remove_pv(vg_t * vg_ptr, pv_t * pv)
274 if (!vg_ptr->pv_subdir_pde)
277 _make_pv_name(pv->pv_name, name, name + sizeof(name));
278 remove_proc_entry(name, vg_ptr->pv_subdir_pde);
282 static int _proc_read_vg(char *page, char **start, off_t off,
283 int count, int *eof, void *data)
289 sz += sprintf(page + sz, "name: %s\n", vg_ptr->vg_name);
290 sz += sprintf(page + sz, "size: %u\n",
291 vg_ptr->pe_total * vg_ptr->pe_size / 2);
292 sz += sprintf(page + sz, "access: %u\n", vg_ptr->vg_access);
293 sz += sprintf(page + sz, "status: %u\n", vg_ptr->vg_status);
294 sz += sprintf(page + sz, "number: %u\n", vg_ptr->vg_number);
295 sz += sprintf(page + sz, "LV max: %u\n", vg_ptr->lv_max);
296 sz += sprintf(page + sz, "LV current: %u\n", vg_ptr->lv_cur);
297 sz += sprintf(page + sz, "LV open: %u\n", vg_ptr->lv_open);
298 sz += sprintf(page + sz, "PV max: %u\n", vg_ptr->pv_max);
299 sz += sprintf(page + sz, "PV current: %u\n", vg_ptr->pv_cur);
300 sz += sprintf(page + sz, "PV active: %u\n", vg_ptr->pv_act);
302 sprintf(page + sz, "PE size: %u\n", vg_ptr->pe_size / 2);
303 sz += sprintf(page + sz, "PE total: %u\n", vg_ptr->pe_total);
305 sprintf(page + sz, "PE allocated: %u\n", vg_ptr->pe_allocated);
307 _show_uuid(vg_ptr->vg_uuid, uuid, uuid + sizeof(uuid));
308 sz += sprintf(page + sz, "uuid: %s\n", uuid);
313 static int _proc_read_lv(char *page, char **start, off_t off,
314 int count, int *eof, void *data)
319 sz += sprintf(page + sz, "name: %s\n", lv->lv_name);
320 sz += sprintf(page + sz, "size: %u\n", lv->lv_size);
321 sz += sprintf(page + sz, "access: %u\n", lv->lv_access);
322 sz += sprintf(page + sz, "status: %u\n", lv->lv_status);
323 sz += sprintf(page + sz, "number: %u\n", lv->lv_number);
324 sz += sprintf(page + sz, "open: %u\n", lv->lv_open);
325 sz += sprintf(page + sz, "allocation: %u\n", lv->lv_allocation);
326 if (lv->lv_stripes > 1) {
327 sz += sprintf(page + sz, "stripes: %u\n",
329 sz += sprintf(page + sz, "stripesize: %u\n",
332 sz += sprintf(page + sz, "device: %02u:%02u\n",
333 MAJOR(lv->lv_dev), MINOR(lv->lv_dev));
338 static int _proc_read_pv(char *page, char **start, off_t off,
339 int count, int *eof, void *data)
345 sz += sprintf(page + sz, "name: %s\n", pv->pv_name);
346 sz += sprintf(page + sz, "size: %u\n", pv->pv_size);
347 sz += sprintf(page + sz, "status: %u\n", pv->pv_status);
348 sz += sprintf(page + sz, "number: %u\n", pv->pv_number);
349 sz += sprintf(page + sz, "allocatable: %u\n", pv->pv_allocatable);
350 sz += sprintf(page + sz, "LV current: %u\n", pv->lv_cur);
351 sz += sprintf(page + sz, "PE size: %u\n", pv->pe_size / 2);
352 sz += sprintf(page + sz, "PE total: %u\n", pv->pe_total);
353 sz += sprintf(page + sz, "PE allocated: %u\n", pv->pe_allocated);
354 sz += sprintf(page + sz, "device: %02u:%02u\n",
355 MAJOR(pv->pv_dev), MINOR(pv->pv_dev));
357 _show_uuid(pv->pv_uuid, uuid, uuid + sizeof(uuid));
358 sz += sprintf(page + sz, "uuid: %s\n", uuid);
363 static int _proc_read_global(char *page, char **start, off_t pos,
364 int count, int *eof, void *data)
367 #define LVM_PROC_BUF ( i == 0 ? dummy_buf : &buf[sz])
369 int c, i, l, p, v, vg_counter, pv_counter, lv_counter,
370 lv_open_counter, lv_open_total, pe_t_bytes, hash_table_bytes,
371 lv_block_exception_t_bytes, seconds;
374 static char *buf = NULL;
375 static char dummy_buf[160]; /* sized for 2 lines */
381 #ifdef DEBUG_LVM_PROC_GET_INFO
383 "%s - lvm_proc_get_global_info CALLED pos: %lu count: %d\n",
384 lvm_name, pos, count);
387 if (pos != 0 && buf != NULL)
390 sz_last = vg_counter = pv_counter = lv_counter = lv_open_counter =
391 lv_open_total = pe_t_bytes = hash_table_bytes =
392 lv_block_exception_t_bytes = 0;
394 /* get some statistics */
395 for (v = 0; v < ABS_MAX_VG; v++) {
396 if ((vg_ptr = vg[v]) != NULL) {
398 pv_counter += vg_ptr->pv_cur;
399 lv_counter += vg_ptr->lv_cur;
400 if (vg_ptr->lv_cur > 0) {
401 for (l = 0; l < vg[v]->lv_max; l++) {
403 vg_ptr->lv[l]) != NULL) {
409 lv_snapshot_hash_table_size;
411 lv_block_exception !=
413 lv_block_exception_t_bytes
417 if (lv_ptr->lv_open > 0) {
429 pe_t_bytes *= sizeof(pe_t);
430 lv_block_exception_t_bytes *= sizeof(lv_block_exception_t);
433 P_KFREE("%s -- vfree %d\n", lvm_name, __LINE__);
439 /* 2 times: first to get size to allocate buffer,
440 2nd to fill the malloced buffer */
441 for (i = 0; i < 2; i++) {
443 sz += sprintf(LVM_PROC_BUF, "LVM "
450 "Total: %d VG%s %d PV%s %d LV%s ",
452 vg_counter, vg_counter == 1 ? "" : "s",
453 pv_counter, pv_counter == 1 ? "" : "s",
454 lv_counter, lv_counter == 1 ? "" : "s");
455 sz += sprintf(LVM_PROC_BUF,
458 lv_open_counter == 1 ? "" : "s");
459 if (lv_open_total > 0)
460 sz += sprintf(LVM_PROC_BUF,
461 " %d times)\n", lv_open_total);
463 sz += sprintf(LVM_PROC_BUF, ")");
464 sz += sprintf(LVM_PROC_BUF,
465 "\nGlobal: %lu bytes malloced IOP version: %d ",
466 vg_counter * sizeof(vg_t) +
467 pv_counter * sizeof(pv_t) +
468 lv_counter * sizeof(lv_t) +
469 pe_t_bytes + hash_table_bytes +
470 lv_block_exception_t_bytes + sz_last,
473 seconds = CURRENT_TIME - loadtime;
475 loadtime = CURRENT_TIME + seconds;
476 if (seconds / 86400 > 0) {
477 sz += sprintf(LVM_PROC_BUF, "%d day%s ",
479 seconds / 86400 == 0 ||
480 seconds / 86400 > 1 ? "s" : "");
482 sz += sprintf(LVM_PROC_BUF, "%d:%02d:%02d active\n",
483 (seconds % 86400) / 3600,
484 (seconds % 3600) / 60, seconds % 60);
486 if (vg_counter > 0) {
487 for (v = 0; v < ABS_MAX_VG; v++) {
489 if ((vg_ptr = vg[v]) != NULL) {
491 _vg_info(vg_ptr, LVM_PROC_BUF);
493 /* physical volumes */
494 sz += sprintf(LVM_PROC_BUF,
499 for (p = 0; p < vg_ptr->pv_max;
519 /* logical volumes */
520 sz += sprintf(LVM_PROC_BUF,
525 for (l = 0; l < vg_ptr->lv_max;
544 if (vg_ptr->lv_cur == 0)
546 sprintf(LVM_PROC_BUF,
548 sz += sprintf(LVM_PROC_BUF, "\n");
559 "%s - vmalloc error at line %d\n",
575 if (sz - pos < count)
584 * provide VG info for proc filesystem use (global)
586 static int _vg_info(vg_t * vg_ptr, char *buf)
589 char inactive_flag = ' ';
591 if (!(vg_ptr->vg_status & VG_ACTIVE))
594 "\nVG: %c%s [%d PV, %d LV/%d open] "
596 " Usage [KB/PE]: %d /%d total "
597 "%d /%d used %d /%d free",
603 vg_ptr->pe_size >> 1,
604 vg_ptr->pe_size * vg_ptr->pe_total >> 1,
606 vg_ptr->pe_allocated * vg_ptr->pe_size >> 1,
607 vg_ptr->pe_allocated,
608 (vg_ptr->pe_total - vg_ptr->pe_allocated) *
609 vg_ptr->pe_size >> 1,
610 vg_ptr->pe_total - vg_ptr->pe_allocated);
616 * provide LV info for proc filesystem use (global)
618 static int _lv_info(vg_t * vg_ptr, lv_t * lv_ptr, char *buf)
621 char inactive_flag = 'A', allocation_flag = ' ',
622 stripes_flag = ' ', rw_flag = ' ', *basename;
624 if (!(lv_ptr->lv_status & LV_ACTIVE))
627 if (lv_ptr->lv_access & LV_WRITE)
629 allocation_flag = 'D';
630 if (lv_ptr->lv_allocation & LV_CONTIGUOUS)
631 allocation_flag = 'C';
633 if (lv_ptr->lv_stripes > 1)
635 sz += sprintf(buf + sz,
638 rw_flag, allocation_flag, stripes_flag);
639 if (lv_ptr->lv_stripes > 1)
640 sz += sprintf(buf + sz, "%-2d", lv_ptr->lv_stripes);
642 sz += sprintf(buf + sz, " ");
644 /* FIXME: use _basename */
645 basename = strrchr(lv_ptr->lv_name, '/');
647 basename = lv_ptr->lv_name;
650 sz += sprintf(buf + sz, "] %-25s", basename);
651 if (strlen(basename) > 25)
652 sz += sprintf(buf + sz,
654 sz += sprintf(buf + sz, "%9d /%-6d ",
655 lv_ptr->lv_size >> 1,
656 lv_ptr->lv_size / vg_ptr->pe_size);
658 if (lv_ptr->lv_open == 0)
659 sz += sprintf(buf + sz, "close");
661 sz += sprintf(buf + sz, "%dx open", lv_ptr->lv_open);
668 * provide PV info for proc filesystem use (global)
670 static int _pv_info(pv_t * pv, char *buf)
673 char inactive_flag = 'A', allocation_flag = ' ';
674 char *pv_name = NULL;
676 if (!(pv->pv_status & PV_ACTIVE))
678 allocation_flag = 'A';
679 if (!(pv->pv_allocatable & PV_ALLOCATABLE))
680 allocation_flag = 'N';
681 pv_name = strchr(pv->pv_name + 1, '/');
683 pv_name = pv->pv_name;
687 "[%c%c] %-21s %8d /%-6d "
688 "%8d /%-6d %8d /%-6d",
692 pv->pe_total * pv->pe_size >> 1,
694 pv->pe_allocated * pv->pe_size >> 1,
696 (pv->pe_total - pv->pe_allocated) *
697 pv->pe_size >> 1, pv->pe_total - pv->pe_allocated);
701 static void _show_uuid(const char *src, char *b, char *e)
706 for (i = 0; *src && (b != e); i++) {