1/*
2 * kernel/lvm-fs.c
3 *
4 * Copyright (C) 2001-2002 Sistina Software
5 *
6 * January-May,December 2001
7 * May 2002
8 *
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)
12 * any later version.
13 *
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.
18 *
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.
23 *
24 */
25
26/*
27 * Changelog
28 *
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)
35 *
36 */
37
38#include <linux/config.h>
39#include <linux/version.h>
40
41#include <linux/kernel.h>
42#include <linux/vmalloc.h>
43#include <linux/smp_lock.h>
44
45#include <linux/devfs_fs_kernel.h>
46#include <linux/proc_fs.h>
47#include <linux/init.h>
48#include <linux/lvm.h>
49
50#include "lvm-internal.h"
51
52
53static int _proc_read_vg(char *page, char **start, off_t off,
54			 int count, int *eof, void *data);
55static int _proc_read_lv(char *page, char **start, off_t off,
56			 int count, int *eof, void *data);
57static int _proc_read_pv(char *page, char **start, off_t off,
58			 int count, int *eof, void *data);
59static int _proc_read_global(char *page, char **start, off_t off,
60			     int count, int *eof, void *data);
61
62static int _vg_info(vg_t *vg_ptr, char *buf);
63static int _lv_info(vg_t *vg_ptr, lv_t *lv_ptr, char *buf);
64static int _pv_info(pv_t *pv_ptr, char *buf);
65
66static void _show_uuid(const char *src, char *b, char *e);
67
68static devfs_handle_t vg_devfs_handle[MAX_VG];
69static devfs_handle_t ch_devfs_handle[MAX_VG];
70static devfs_handle_t lv_devfs_handle[MAX_LV];
71
72static struct proc_dir_entry *lvm_proc_dir = NULL;
73static struct proc_dir_entry *lvm_proc_vg_subdir = NULL;
74
75/* inline functions */
76
77/* public interface */
78void __init lvm_init_fs() {
79	struct proc_dir_entry *pde;
80
81/* User-space has already registered this */
82	lvm_proc_dir = create_proc_entry(LVM_DIR, S_IFDIR, &proc_root);
83	if (lvm_proc_dir) {
84		lvm_proc_vg_subdir = create_proc_entry(LVM_VG_SUBDIR, S_IFDIR,
85						       lvm_proc_dir);
86		pde = create_proc_entry(LVM_GLOBAL, S_IFREG, lvm_proc_dir);
87		if ( pde != NULL) pde->read_proc = _proc_read_global;
88	}
89}
90
91void lvm_fin_fs() {
92	remove_proc_entry(LVM_GLOBAL, lvm_proc_dir);
93	remove_proc_entry(LVM_VG_SUBDIR, lvm_proc_dir);
94	remove_proc_entry(LVM_DIR, &proc_root);
95}
96
97void lvm_fs_create_vg(vg_t *vg_ptr) {
98	struct proc_dir_entry *pde;
99
100	if (!vg_ptr)
101		return;
102
103	vg_devfs_handle[vg_ptr->vg_number] =
104		devfs_mk_dir(0, vg_ptr->vg_name, NULL);
105
106	ch_devfs_handle[vg_ptr->vg_number] = devfs_register(
107		vg_devfs_handle[vg_ptr->vg_number] , "group",
108		DEVFS_FL_DEFAULT, LVM_CHAR_MAJOR, vg_ptr->vg_number,
109		S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP,
110		&lvm_chr_fops, NULL);
111
112	vg_ptr->vg_dir_pde = create_proc_entry(vg_ptr->vg_name, S_IFDIR,
113					       lvm_proc_vg_subdir);
114
115	if((pde = create_proc_entry("group", S_IFREG, vg_ptr->vg_dir_pde))) {
116		pde->read_proc = _proc_read_vg;
117		pde->data = vg_ptr;
118	}
119
120	vg_ptr->lv_subdir_pde =
121		create_proc_entry(LVM_LV_SUBDIR, S_IFDIR, vg_ptr->vg_dir_pde);
122
123	vg_ptr->pv_subdir_pde =
124		create_proc_entry(LVM_PV_SUBDIR, S_IFDIR, vg_ptr->vg_dir_pde);
125}
126
127void lvm_fs_remove_vg(vg_t *vg_ptr) {
128	int i;
129
130	if (!vg_ptr)
131		return;
132
133	devfs_unregister(ch_devfs_handle[vg_ptr->vg_number]);
134	ch_devfs_handle[vg_ptr->vg_number] = NULL;
135
136	/* remove lv's */
137	for(i = 0; i < vg_ptr->lv_max; i++)
138		if(vg_ptr->lv[i]) lvm_fs_remove_lv(vg_ptr, vg_ptr->lv[i]);
139
140	/* must not remove directory before leaf nodes */
141	devfs_unregister(vg_devfs_handle[vg_ptr->vg_number]);
142	vg_devfs_handle[vg_ptr->vg_number] = NULL;
143
144	/* remove pv's */
145	for(i = 0; i < vg_ptr->pv_max; i++)
146		if(vg_ptr->pv[i]) lvm_fs_remove_pv(vg_ptr, vg_ptr->pv[i]);
147
148	if(vg_ptr->vg_dir_pde) {
149		remove_proc_entry(LVM_LV_SUBDIR, vg_ptr->vg_dir_pde);
150		vg_ptr->lv_subdir_pde = NULL;
151
152		remove_proc_entry(LVM_PV_SUBDIR, vg_ptr->vg_dir_pde);
153		vg_ptr->pv_subdir_pde = NULL;
154
155		remove_proc_entry("group", vg_ptr->vg_dir_pde);
156		vg_ptr->vg_dir_pde = NULL;
157
158		remove_proc_entry(vg_ptr->vg_name, lvm_proc_vg_subdir);
159	}
160}
161
162
163static inline const char *_basename(const char *str) {
164	const char *name = strrchr(str, '/');
165	name = name ? name + 1 : str;
166	return name;
167}
168
169devfs_handle_t lvm_fs_create_lv(vg_t *vg_ptr, lv_t *lv) {
170	struct proc_dir_entry *pde;
171	const char *name;
172
173	if (!vg_ptr || !lv)
174		return NULL;
175
176	name = _basename(lv->lv_name);
177
178	lv_devfs_handle[MINOR(lv->lv_dev)] = devfs_register(
179		vg_devfs_handle[vg_ptr->vg_number], name,
180		DEVFS_FL_DEFAULT, LVM_BLK_MAJOR, MINOR(lv->lv_dev),
181		S_IFBLK | S_IRUSR | S_IWUSR | S_IRGRP,
182		&lvm_blk_dops, NULL);
183
184	if(vg_ptr->lv_subdir_pde &&
185	   (pde = create_proc_entry(name, S_IFREG, vg_ptr->lv_subdir_pde))) {
186		pde->read_proc = _proc_read_lv;
187		pde->data = lv;
188	}
189	return lv_devfs_handle[MINOR(lv->lv_dev)];
190}
191
192void lvm_fs_remove_lv(vg_t *vg_ptr, lv_t *lv) {
193
194	if (!vg_ptr || !lv)
195		return;
196
197	devfs_unregister(lv_devfs_handle[MINOR(lv->lv_dev)]);
198	lv_devfs_handle[MINOR(lv->lv_dev)] = NULL;
199
200	if(vg_ptr->lv_subdir_pde) {
201		const char *name = _basename(lv->lv_name);
202		remove_proc_entry(name, vg_ptr->lv_subdir_pde);
203	}
204}
205
206
207static inline void _make_pv_name(const char *src, char *b, char *e) {
208	int offset = strlen(LVM_DIR_PREFIX);
209	if(strncmp(src, LVM_DIR_PREFIX, offset))
210		offset = 0;
211
212	e--;
213	src += offset;
214	while(*src && (b != e)) {
215		*b++ = (*src == '/') ? '_' : *src;
216		src++;
217	}
218	*b = '\0';
219}
220
221void lvm_fs_create_pv(vg_t *vg_ptr, pv_t *pv) {
222	struct proc_dir_entry *pde;
223	char name[NAME_LEN];
224
225	if (!vg_ptr || !pv)
226		return;
227
228	if(!vg_ptr->pv_subdir_pde)
229		return;
230
231	_make_pv_name(pv->pv_name, name, name + sizeof(name));
232	if((pde = create_proc_entry(name, S_IFREG, vg_ptr->pv_subdir_pde))) {
233		pde->read_proc = _proc_read_pv;
234		pde->data = pv;
235	}
236}
237
238void lvm_fs_remove_pv(vg_t *vg_ptr, pv_t *pv) {
239	char name[NAME_LEN];
240
241	if (!vg_ptr || !pv)
242		return;
243
244	if(!vg_ptr->pv_subdir_pde)
245		return;
246
247	_make_pv_name(pv->pv_name, name, name + sizeof(name));
248	remove_proc_entry(name, vg_ptr->pv_subdir_pde);
249}
250
251
252static int _proc_read_vg(char *page, char **start, off_t off,
253			  int count, int *eof, void *data) {
254	int sz = 0;
255	vg_t *vg_ptr = data;
256	char uuid[NAME_LEN];
257
258	sz += sprintf(page + sz, "name:         %s\n", vg_ptr->vg_name);
259	sz += sprintf(page + sz, "size:         %u\n",
260		      vg_ptr->pe_total * vg_ptr->pe_size / 2);
261	sz += sprintf(page + sz, "access:       %u\n", vg_ptr->vg_access);
262	sz += sprintf(page + sz, "status:       %u\n", vg_ptr->vg_status);
263	sz += sprintf(page + sz, "number:       %u\n", vg_ptr->vg_number);
264	sz += sprintf(page + sz, "LV max:       %u\n", vg_ptr->lv_max);
265	sz += sprintf(page + sz, "LV current:   %u\n", vg_ptr->lv_cur);
266	sz += sprintf(page + sz, "LV open:      %u\n", vg_ptr->lv_open);
267	sz += sprintf(page + sz, "PV max:       %u\n", vg_ptr->pv_max);
268	sz += sprintf(page + sz, "PV current:   %u\n", vg_ptr->pv_cur);
269	sz += sprintf(page + sz, "PV active:    %u\n", vg_ptr->pv_act);
270	sz += sprintf(page + sz, "PE size:      %u\n", vg_ptr->pe_size / 2);
271	sz += sprintf(page + sz, "PE total:     %u\n", vg_ptr->pe_total);
272	sz += sprintf(page + sz, "PE allocated: %u\n", vg_ptr->pe_allocated);
273
274	_show_uuid(vg_ptr->vg_uuid, uuid, uuid + sizeof(uuid));
275	sz += sprintf(page + sz, "uuid:         %s\n", uuid);
276
277	return sz;
278}
279
280static int _proc_read_lv(char *page, char **start, off_t off,
281			  int count, int *eof, void *data) {
282	int sz = 0;
283	lv_t *lv = data;
284
285	sz += sprintf(page + sz, "name:         %s\n", lv->lv_name);
286	sz += sprintf(page + sz, "size:         %u\n", lv->lv_size);
287	sz += sprintf(page + sz, "access:       %u\n", lv->lv_access);
288	sz += sprintf(page + sz, "status:       %u\n", lv->lv_status);
289	sz += sprintf(page + sz, "number:       %u\n", lv->lv_number);
290	sz += sprintf(page + sz, "open:         %u\n", lv->lv_open);
291	sz += sprintf(page + sz, "allocation:   %u\n", lv->lv_allocation);
292	if(lv->lv_stripes > 1) {
293		sz += sprintf(page + sz, "stripes:      %u\n",
294			      lv->lv_stripes);
295		sz += sprintf(page + sz, "stripesize:   %u\n",
296			      lv->lv_stripesize);
297	}
298	sz += sprintf(page + sz, "device:       %02u:%02u\n",
299		      MAJOR(lv->lv_dev), MINOR(lv->lv_dev));
300
301	return sz;
302}
303
304static int _proc_read_pv(char *page, char **start, off_t off,
305			 int count, int *eof, void *data) {
306	int sz = 0;
307	pv_t *pv = data;
308	char uuid[NAME_LEN];
309
310	sz += sprintf(page + sz, "name:         %s\n", pv->pv_name);
311	sz += sprintf(page + sz, "size:         %u\n", pv->pv_size);
312	sz += sprintf(page + sz, "status:       %u\n", pv->pv_status);
313	sz += sprintf(page + sz, "number:       %u\n", pv->pv_number);
314	sz += sprintf(page + sz, "allocatable:  %u\n", pv->pv_allocatable);
315	sz += sprintf(page + sz, "LV current:   %u\n", pv->lv_cur);
316	sz += sprintf(page + sz, "PE size:      %u\n", pv->pe_size / 2);
317	sz += sprintf(page + sz, "PE total:     %u\n", pv->pe_total);
318	sz += sprintf(page + sz, "PE allocated: %u\n", pv->pe_allocated);
319	sz += sprintf(page + sz, "device:       %02u:%02u\n",
320                      MAJOR(pv->pv_dev), MINOR(pv->pv_dev));
321
322	_show_uuid(pv->pv_uuid, uuid, uuid + sizeof(uuid));
323	sz += sprintf(page + sz, "uuid:         %s\n", uuid);
324
325	return sz;
326}
327
328static int _proc_read_global(char *page, char **start, off_t pos, int count,
329			     int *eof, void *data) {
330
331#define  LVM_PROC_BUF   ( i == 0 ? dummy_buf : &buf[sz])
332
333	int c, i, l, p, v, vg_counter, pv_counter, lv_counter, lv_open_counter,
334		lv_open_total, pe_t_bytes, hash_table_bytes, lv_block_exception_t_bytes, seconds;
335	static off_t sz;
336	off_t sz_last;
337	static char *buf = NULL;
338	static char dummy_buf[160];	/* sized for 2 lines */
339	vg_t *vg_ptr;
340	lv_t *lv_ptr;
341	pv_t *pv_ptr;
342
343
344#ifdef DEBUG_LVM_PROC_GET_INFO
345	printk(KERN_DEBUG
346	       "%s - lvm_proc_get_global_info CALLED  pos: %lu  count: %d\n",
347	       lvm_name, pos, count);
348#endif
349
350	if(pos != 0 && buf != NULL)
351		goto out;
352
353	sz_last = vg_counter = pv_counter = lv_counter = lv_open_counter = \
354		lv_open_total = pe_t_bytes = hash_table_bytes = \
355		lv_block_exception_t_bytes = 0;
356
357	/* get some statistics */
358	for (v = 0; v < ABS_MAX_VG; v++) {
359		if ((vg_ptr = vg[v]) != NULL) {
360			vg_counter++;
361			pv_counter += vg_ptr->pv_cur;
362			lv_counter += vg_ptr->lv_cur;
363			if (vg_ptr->lv_cur > 0) {
364				for (l = 0; l < vg[v]->lv_max; l++) {
365					if ((lv_ptr = vg_ptr->lv[l]) != NULL) {
366						pe_t_bytes += lv_ptr->lv_allocated_le;
367						hash_table_bytes += lv_ptr->lv_snapshot_hash_table_size;
368						if (lv_ptr->lv_block_exception != NULL)
369							lv_block_exception_t_bytes += lv_ptr->lv_remap_end;
370						if (lv_ptr->lv_open > 0) {
371							lv_open_counter++;
372							lv_open_total += lv_ptr->lv_open;
373						}
374					}
375				}
376			}
377		}
378	}
379
380	pe_t_bytes *= sizeof(pe_t);
381	lv_block_exception_t_bytes *= sizeof(lv_block_exception_t);
382
383	if (buf != NULL) {
384		P_KFREE("%s -- vfree %d\n", lvm_name, __LINE__);
385		lock_kernel();
386		vfree(buf);
387		unlock_kernel();
388		buf = NULL;
389	}
390	/* 2 times: first to get size to allocate buffer,
391	   2nd to fill the malloced buffer */
392	for (i = 0; i < 2; i++) {
393		sz = 0;
394		sz += sprintf(LVM_PROC_BUF,
395			      "LVM "
396#ifdef MODULE
397			      "module"
398#else
399			      "driver"
400#endif
401			      " %s\n\n"
402			      "Total:  %d VG%s  %d PV%s  %d LV%s ",
403			      lvm_version,
404			      vg_counter, vg_counter == 1 ? "" : "s",
405			      pv_counter, pv_counter == 1 ? "" : "s",
406			      lv_counter, lv_counter == 1 ? "" : "s");
407		sz += sprintf(LVM_PROC_BUF,
408			      "(%d LV%s open",
409			      lv_open_counter,
410			      lv_open_counter == 1 ? "" : "s");
411		if (lv_open_total > 0)
412			sz += sprintf(LVM_PROC_BUF,
413				      " %d times)\n",
414				      lv_open_total);
415		else
416			sz += sprintf(LVM_PROC_BUF, ")");
417		sz += sprintf(LVM_PROC_BUF,
418			      "\nGlobal: %lu bytes malloced   IOP version: %d   ",
419			      vg_counter * sizeof(vg_t) +
420			      pv_counter * sizeof(pv_t) +
421			      lv_counter * sizeof(lv_t) +
422			      pe_t_bytes + hash_table_bytes + lv_block_exception_t_bytes + sz_last,
423			      lvm_iop_version);
424
425		seconds = CURRENT_TIME - loadtime;
426		if (seconds < 0)
427			loadtime = CURRENT_TIME + seconds;
428		if (seconds / 86400 > 0) {
429			sz += sprintf(LVM_PROC_BUF, "%d day%s ",
430				      seconds / 86400,
431				      seconds / 86400 == 0 ||
432				      seconds / 86400 > 1 ? "s" : "");
433		}
434		sz += sprintf(LVM_PROC_BUF, "%d:%02d:%02d active\n",
435			      (seconds % 86400) / 3600,
436			      (seconds % 3600) / 60,
437			      seconds % 60);
438
439		if (vg_counter > 0) {
440			for (v = 0; v < ABS_MAX_VG; v++) {
441				/* volume group */
442				if ((vg_ptr = vg[v]) != NULL) {
443					sz += _vg_info(vg_ptr, LVM_PROC_BUF);
444
445					/* physical volumes */
446					sz += sprintf(LVM_PROC_BUF,
447						      "\n  PV%s ",
448						      vg_ptr->pv_cur == 1 ? ": " : "s:");
449					c = 0;
450					for (p = 0; p < vg_ptr->pv_max; p++) {
451						if ((pv_ptr = vg_ptr->pv[p]) != NULL) {
452							sz += _pv_info(pv_ptr, LVM_PROC_BUF);
453
454							c++;
455							if (c < vg_ptr->pv_cur)
456								sz += sprintf(LVM_PROC_BUF,
457									      "\n       ");
458						}
459					}
460
461					/* logical volumes */
462					sz += sprintf(LVM_PROC_BUF,
463						      "\n    LV%s ",
464						      vg_ptr->lv_cur == 1 ? ": " : "s:");
465					c = 0;
466					for (l = 0; l < vg_ptr->lv_max; l++) {
467						if ((lv_ptr = vg_ptr->lv[l]) != NULL) {
468							sz += _lv_info(vg_ptr, lv_ptr, LVM_PROC_BUF);
469							c++;
470							if (c < vg_ptr->lv_cur)
471								sz += sprintf(LVM_PROC_BUF,
472									      "\n         ");
473						}
474					}
475					if (vg_ptr->lv_cur == 0) sz += sprintf(LVM_PROC_BUF, "none");
476					sz += sprintf(LVM_PROC_BUF, "\n");
477				}
478			}
479		}
480		if (buf == NULL) {
481			lock_kernel();
482			buf = vmalloc(sz);
483			unlock_kernel();
484			if (buf == NULL) {
485				sz = 0;
486				return sprintf(page, "%s - vmalloc error at line %d\n",
487					       lvm_name, __LINE__);
488			}
489		}
490		sz_last = sz;
491	}
492
493 out:
494	if (pos > sz - 1) {
495		lock_kernel();
496		vfree(buf);
497		unlock_kernel();
498		buf = NULL;
499		return 0;
500	}
501	*start = &buf[pos];
502	if (sz - pos < count)
503		return sz - pos;
504	else
505		return count;
506
507#undef LVM_PROC_BUF
508}
509
510/*
511 * provide VG info for proc filesystem use (global)
512 */
513static int _vg_info(vg_t *vg_ptr, char *buf) {
514	int sz = 0;
515	char inactive_flag = ' ';
516
517	if (!(vg_ptr->vg_status & VG_ACTIVE)) inactive_flag = 'I';
518	sz = sprintf(buf,
519		     "\nVG: %c%s  [%d PV, %d LV/%d open] "
520		     " PE Size: %d KB\n"
521		     "  Usage [KB/PE]: %d /%d total  "
522		     "%d /%d used  %d /%d free",
523		     inactive_flag,
524		     vg_ptr->vg_name,
525		     vg_ptr->pv_cur,
526		     vg_ptr->lv_cur,
527		     vg_ptr->lv_open,
528	     	     vg_ptr->pe_size >> 1,
529		     vg_ptr->pe_size * vg_ptr->pe_total >> 1,
530		     vg_ptr->pe_total,
531		     vg_ptr->pe_allocated * vg_ptr->pe_size >> 1,
532	     	     vg_ptr->pe_allocated,
533		     (vg_ptr->pe_total - vg_ptr->pe_allocated) *
534	     	     vg_ptr->pe_size >> 1,
535		     vg_ptr->pe_total - vg_ptr->pe_allocated);
536	return sz;
537}
538
539
540/*
541 * provide LV info for proc filesystem use (global)
542 */
543static int _lv_info(vg_t *vg_ptr, lv_t *lv_ptr, char *buf) {
544	int sz = 0;
545	char inactive_flag = 'A', allocation_flag = ' ',
546		stripes_flag = ' ', rw_flag = ' ', *basename;
547
548	if (!(lv_ptr->lv_status & LV_ACTIVE))
549		inactive_flag = 'I';
550	rw_flag = 'R';
551	if (lv_ptr->lv_access & LV_WRITE)
552		rw_flag = 'W';
553	allocation_flag = 'D';
554	if (lv_ptr->lv_allocation & LV_CONTIGUOUS)
555		allocation_flag = 'C';
556	stripes_flag = 'L';
557	if (lv_ptr->lv_stripes > 1)
558		stripes_flag = 'S';
559	sz += sprintf(buf+sz,
560		      "[%c%c%c%c",
561		      inactive_flag,
562	 rw_flag,
563		      allocation_flag,
564		      stripes_flag);
565	if (lv_ptr->lv_stripes > 1)
566		sz += sprintf(buf+sz, "%-2d",
567			      lv_ptr->lv_stripes);
568	else
569		sz += sprintf(buf+sz, "  ");
570
571	basename = strrchr(lv_ptr->lv_name, '/');
572	if ( basename == 0) basename = lv_ptr->lv_name;
573	else                basename++;
574	sz += sprintf(buf+sz, "] %-25s", basename);
575	if (strlen(basename) > 25)
576		sz += sprintf(buf+sz,
577			      "\n                              ");
578	sz += sprintf(buf+sz, "%9d /%-6d   ",
579		      lv_ptr->lv_size >> 1,
580		      lv_ptr->lv_size / vg_ptr->pe_size);
581
582	if (lv_ptr->lv_open == 0)
583		sz += sprintf(buf+sz, "close");
584	else
585		sz += sprintf(buf+sz, "%dx open",
586			      lv_ptr->lv_open);
587
588	return sz;
589}
590
591
592/*
593 * provide PV info for proc filesystem use (global)
594 */
595static int _pv_info(pv_t *pv, char *buf) {
596	int sz = 0;
597	char inactive_flag = 'A', allocation_flag = ' ';
598	char *pv_name = NULL;
599
600	if (!(pv->pv_status & PV_ACTIVE))
601		inactive_flag = 'I';
602	allocation_flag = 'A';
603	if (!(pv->pv_allocatable & PV_ALLOCATABLE))
604		allocation_flag = 'N';
605	pv_name = strchr(pv->pv_name+1,'/');
606	if ( pv_name == 0) pv_name = pv->pv_name;
607	else               pv_name++;
608	sz = sprintf(buf,
609		     "[%c%c] %-21s %8d /%-6d  "
610		     "%8d /%-6d  %8d /%-6d",
611		     inactive_flag,
612		     allocation_flag,
613		     pv_name,
614		     pv->pe_total * pv->pe_size >> 1,
615		     pv->pe_total,
616		     pv->pe_allocated * pv->pe_size >> 1,
617		     pv->pe_allocated,
618		     (pv->pe_total - pv->pe_allocated) *
619		     pv->pe_size >> 1,
620		     pv->pe_total - pv->pe_allocated);
621	return sz;
622}
623
624static void _show_uuid(const char *src, char *b, char *e) {
625	int i;
626
627	e--;
628	for(i = 0; *src && (b != e); i++) {
629		if(i && !(i & 0x3))
630			*b++ = '-';
631		*b++ = *src++;
632	}
633	*b = '\0';
634}
635