--- 2.4.3aa/include/linux/lvm.h.orig Mon Apr 2 20:33:32 2001 +++ 2.4.3aa/include/linux/lvm.h Fri Apr 6 04:37:24 2001 @@ -3,28 +3,28 @@ * kernel/lvm.h * tools/lib/lvm.h * - * Copyright (C) 1997 - 2000 Heinz Mauelshagen, Sistina Software + * Copyright (C) 1997 - 2001 Heinz Mauelshagen, Sistina Software * * February-November 1997 * May-July 1998 * January-March,July,September,October,Dezember 1999 * January,February,July,November 2000 - * January 2001 + * January-March 2001 * * lvm is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. - * + * * lvm is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with GNU CC; see the file COPYING. If not, write to * the Free Software Foundation, 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * Boston, MA 02111-1307, USA. * */ @@ -52,7 +52,7 @@ * 08/12/1999 - changed LVM_LV_SIZE_MAX macro to reflect current 1TB limit * 01/01/2000 - extended lv_v2 core structure by wait_queue member * 12/02/2000 - integrated Andrea Arcagnelli's snapshot work - * 18/02/2000 - seperated user and kernel space parts by + * 18/02/2000 - seperated user and kernel space parts by * #ifdef them with __KERNEL__ * 08/03/2000 - implemented cluster/shared bits for vg_access * 26/06/2000 - implemented snapshot persistency and resizing support @@ -60,6 +60,13 @@ * 12/11/2000 - removed unneeded timestamp definitions * 24/12/2000 - removed LVM_TO_{CORE,DISK}*, use cpu_{from, to}_le* * instead - Christoph Hellwig + * 22/01/2001 - Change ulong to uint32_t + * 14/02/2001 - changed LVM_SNAPSHOT_MIN_CHUNK to 1 page + * 20/02/2001 - incremented IOP version to 11 because of incompatible + * change in VG activation (in order to support devfs better) + * 01/03/2001 - Revert to IOP10 and add VG_CREATE_OLD call for compatibility + * 08/03/2001 - new lv_t (in core) version number 5: changed page member + * to (struct kiobuf *) to use for COW exception table io * */ @@ -67,9 +74,11 @@ #ifndef _LVM_H_INCLUDE #define _LVM_H_INCLUDE -#define _LVM_KERNEL_H_VERSION "LVM 0.9.1_beta2 (18/01/2001)" +#define LVM_RELEASE_NAME "0.9.1_beta6" +#define LVM_RELEASE_DATE "12/03/2001" + +#define _LVM_KERNEL_H_VERSION "LVM "LVM_RELEASE_NAME" ("LVM_RELEASE_DATE")" -#include #include /* @@ -90,13 +99,20 @@ #define DEBUG_READ #define DEBUG_GENDISK #define DEBUG_VG_CREATE - #define DEBUG_LVM_BLK_OPEN + #define DEBUG_DEVICE #define DEBUG_KFREE */ #endif /* #ifdef __KERNEL__ */ +#ifndef __KERNEL__ +#define __KERNEL__ +#include +#include +#undef __KERNEL__ +#else #include #include +#endif /* #ifndef __KERNEL__ */ #include #include @@ -117,10 +133,10 @@ #undef BLOCK_SIZE #endif -#ifdef CONFIG_ARCH_S390 +#ifdef CONFIG_ARCH_S390 #define BLOCK_SIZE 4096 #else -#define BLOCK_SIZE 1024 +#define BLOCK_SIZE 512 #endif #ifndef SECTOR_SIZE @@ -141,7 +157,7 @@ /* set the default structure version */ #if ( LVM_STRUCT_VERSION == 1) #define pv_t pv_v2_t -#define lv_t lv_v4_t +#define lv_t lv_v5_t #define vg_t vg_v3_t #define pv_disk_t pv_disk_v2_t #define lv_disk_t lv_disk_v3_t @@ -299,7 +315,7 @@ #define LVM_SNAPSHOT_MAX_CHUNK 1024 /* 1024 KB */ #define LVM_SNAPSHOT_DEF_CHUNK 64 /* 64 KB */ -#define LVM_SNAPSHOT_MIN_CHUNK 1 /* 1 KB */ +#define LVM_SNAPSHOT_MIN_CHUNK (PAGE_SIZE/1024) /* 4 or 8 KB */ #define UNDEF -1 #define FALSE 0 @@ -323,7 +339,7 @@ * ioctls */ /* volume group */ -#define VG_CREATE _IOW ( 0xfe, 0x00, 1) +#define VG_CREATE_OLD _IOW ( 0xfe, 0x00, 1) #define VG_REMOVE _IOW ( 0xfe, 0x01, 1) #define VG_EXTEND _IOW ( 0xfe, 0x03, 1) @@ -336,6 +352,8 @@ #define VG_SET_EXTENDABLE _IOW ( 0xfe, 0x08, 1) #define VG_RENAME _IOW ( 0xfe, 0x09, 1) +/* Since 0.9beta6 */ +#define VG_CREATE _IOW ( 0xfe, 0x0a, 1) /* logical volume */ #define LV_CREATE _IOW ( 0xfe, 0x20, 1) @@ -418,6 +436,9 @@ #define PV_ALLOCATABLE 0x02 /* pv_allocatable */ +/* misc */ +#define LVM_SNAPSHOT_DROPPED_SECTOR 1 + /* * Structure definitions core/disk follow * @@ -440,10 +461,10 @@ /* remap physical sector/rdev pairs including hash */ typedef struct { struct list_head hash; - ulong rsector_org; - kdev_t rdev_org; - ulong rsector_new; - kdev_t rdev_new; + uint32_t rsector_org; + kdev_t rdev_org; + uint32_t rsector_new; + kdev_t rdev_new; } lv_block_exception_v1_t; /* disk stored pe information */ @@ -571,21 +592,21 @@ /* core PE information */ typedef struct { kdev_t dev; - ulong pe; /* to be changed if > 2TB */ - ulong reads; - ulong writes; + uint32_t pe; /* to be changed if > 2TB */ + uint32_t reads; + uint32_t writes; } pe_t; typedef struct { char lv_name[NAME_LEN]; kdev_t old_dev; kdev_t new_dev; - ulong old_pe; - ulong new_pe; + uint32_t old_pe; + uint32_t new_pe; } le_remap_req_t; typedef struct lv_bmap { - ulong lv_block; + uint32_t lv_block; dev_t lv_dev; } lv_bmap_t; @@ -627,11 +648,11 @@ uint lv_snapshot_minor; #ifdef __KERNEL__ struct kiobuf *lv_iobuf; + struct kiobuf *lv_COW_table_iobuf; struct semaphore lv_snapshot_sem; struct list_head *lv_snapshot_hash_table; - ulong lv_snapshot_hash_table_size; - ulong lv_snapshot_hash_mask; - struct page *lv_COW_table_page; + uint32_t lv_snapshot_hash_table_size; + uint32_t lv_snapshot_hash_mask; wait_queue_head_t lv_snapshot_wait; int lv_snapshot_use_rate; void *vg; @@ -640,7 +661,7 @@ #else char dummy[200]; #endif -} lv_v4_t; +} lv_v5_t; /* disk */ typedef struct { @@ -791,7 +812,7 @@ struct { kdev_t lv_dev; kdev_t pv_dev; - ulong pv_offset; + uint32_t pv_offset; } data; } pe_lock_req_t; @@ -804,7 +825,7 @@ /* Request structure LV_STATUS_BYINDEX */ typedef struct { - ulong lv_index; + uint32_t lv_index; lv_t *lv; /* Transfer size because user space and kernel space differ */ ushort size; @@ -813,7 +834,7 @@ /* Request structure LV_STATUS_BYDEV... */ typedef struct { dev_t dev; - pv_t *lv; + lv_t *lv; } lv_status_bydev_req_t; --- 2.4.3aa/drivers/md/lvm.c.orig Sat Feb 10 02:34:09 2001 +++ 2.4.3aa/drivers/md/lvm.c Fri Apr 6 04:37:24 2001 @@ -1,13 +1,13 @@ /* * kernel/lvm.c * - * Copyright (C) 1997 - 2000 Heinz Mauelshagen, Sistina Software + * Copyright (C) 1997 - 2001 Heinz Mauelshagen, Sistina Software * * February-November 1997 * April-May,July-August,November 1998 * January-March,May,July,September,October 1999 * January,February,July,September-November 2000 - * January 2001 + * January,February,March 2001 * * * LVM driver is free software; you can redistribute it and/or modify @@ -43,7 +43,8 @@ * support for free (eg. longer) logical volume names * 12/05/1998 - added spin_locks (thanks to Pascal van Dam * ) - * 25/05/1998 - fixed handling of locked PEs in lvm_map() and lvm_chr_ioctl() + * 25/05/1998 - fixed handling of locked PEs in lvm_map() and + * lvm_chr_ioctl() * 26/05/1998 - reactivated verify_area by access_ok * 07/06/1998 - used vmalloc/vfree instead of kmalloc/kfree to go * beyond 128/256 KB max allocation limit per call @@ -72,7 +73,7 @@ * only other update ioctls are blocked now * - fixed pv->pe to NULL for pv_status * - using lv_req structure in lvm_chr_ioctl() now - * - fixed NULL ptr reference bug in lvm_do_lv_extend_reduce() + * - fixed NULL ptr reference bug in lvm_do_lv_extendreduce() * caused by uncontiguous PV array in lvm_chr_ioctl(VG_REDUCE) * 09/02/1999 - changed BLKRASET and BLKRAGET in lvm_chr_ioctl() to * handle lgoical volume private read ahead sector @@ -125,7 +126,8 @@ * 14/02/2000 - support for 2.3.43 * - integrated Andrea Arcagneli's snapshot code * 25/06/2000 - james (chip) , IKKHAYD! roffl - * 26/06/2000 - enhanced lv_extend_reduce for snapshot logical volume support + * 26/06/2000 - enhanced lv_extend_reduce for snapshot logical volume + * support * 06/09/2000 - added devfs support * 07/09/2000 - changed IOP version to 9 * - started to add new char ioctl LV_STATUS_BYDEV_T to support @@ -148,32 +150,67 @@ * procfs is always supported now. (JT) * 12/01/2001 - avoided flushing logical volume in case of shrinking * because of unecessary overhead in case of heavy updates + * 25/01/2001 - Allow RO open of an inactive LV so it can be reactivated. + * 31/01/2001 - removed blk_init_queue/blk_cleanup_queue queueing will be + * handled by the proper devices. + * - If you try and BMAP a snapshot you now get an -EPERM + * 01/01/2001 - lvm_map() now calls buffer_IO_error on error for 2.4 + * - factored __remap_snapshot out of lvm_map + * 12/02/2001 - move devfs code to create VG before LVs + * 13/02/2001 - allow VG_CREATE on /dev/lvm + * 14/02/2001 - removed modversions.h + * - tidied device defines for blk.h + * - tidied debug statements + * - bug: vg[] member not set back to NULL if activation fails + * - more lvm_map tidying + * 15/02/2001 - register /dev/lvm with devfs correctly (major/minor + * were swapped) + * 19/02/2001 - preallocated buffer_heads for rawio when using + * snapshots [JT] + * 28/02/2001 - introduced the P_DEV macro and changed some internel + * functions to be static [AD] + * 28/02/2001 - factored lvm_get_snapshot_use_rate out of blk_ioctl [AD] + * - fixed user address accessing bug in lvm_do_lv_create() + * where the check for an existing LV takes place right at + * the beginning + * 01/03/2001 - Add VG_CREATE_OLD for IOP 10 compatibility + * 02/03/2001 - Don't destroy usermode pointers in lv_t structures duing + * LV_STATUS_BYxxx + * and remove redundant lv_t variables from same. + * - avoid compilation of lvm_dummy_device_request in case of + * Linux >= 2.3.0 to avoid a warning + * - added lvm_name argument to printk in buffer allocation + * in order to avoid a warning + * 04/03/2001 - moved linux/version.h above first use of KERNEL_VERSION + * macros + * 05/03/2001 - restore copying pe_t array in lvm_do_lv_status_byname. For + * lvdisplay -v (PC) + * - restore copying pe_t array in lvm_do_lv_status_byindex (HM) + * - added copying pe_t array in lvm_do_lv_status_bydev (HM) + * - enhanced lvm_do_lv_status_by{name,index,dev} to be capable + * to copy the lv_block_exception_t array to userspace (HM) + * 08/03/2001 - initialize new lv_ptr->lv_COW_table_iobuf for snapshots; + * removed obsolete lv_ptr->lv_COW_table_page initialization + * - factored lvm_do_pv_flush out of lvm_chr_ioctl (HM) + * 09/03/2001 - Added _lock_open_count to ensure we only drop the lock + * when the locking process closes. * */ +#include -static char *lvm_version = "LVM version 0.9.1_beta2 by Heinz Mauelshagen (18/01/2001)\n"; -static char *lvm_short_version = "version 0.9.1_beta2 (18/01/2001)"; - -#define MAJOR_NR LVM_BLK_MAJOR -#define DEVICE_OFF(device) +#define MAJOR_NR LVM_BLK_MAJOR +#define DEVICE_OFF(device) +#define LOCAL_END_REQUEST /* lvm_do_lv_create calls fsync_dev_lockfs()/unlockfs() */ /* #define LVM_VFS_ENHANCEMENT */ #include -#include - -#ifdef MODVERSIONS -#undef MODULE -#define MODULE -#include -#endif - #include - #include #include + #include #include @@ -184,6 +221,8 @@ #include #include #include + + #include #include #include @@ -199,7 +238,7 @@ #include #include -#include "lvm-snap.h" +#include "lvm-internal.h" #define LVM_CORRECT_READ_AHEAD( a) \ if ( a < LVM_MIN_READ_AHEAD || \ @@ -209,24 +248,6 @@ # define WRITEA WRITE #endif -/* debug macros */ -#ifdef DEBUG_IOCTL -#define P_IOCTL(fmt, args...) printk(KERN_DEBUG "lvm ioctl: " fmt, ## args) -#else -#define P_IOCTL(fmt, args...) -#endif - -#ifdef DEBUG_MAP -#define P_MAP(fmt, args...) printk(KERN_DEBUG "lvm map: " fmt, ## args) -#else -#define P_MAP(fmt, args...) -#endif - -#ifdef DEBUG_KFREE -#define P_KFREE(fmt, args...) printk(KERN_DEBUG "lvm kfree: " fmt, ## args) -#else -#define P_KFREE(fmt, args...) -#endif /* * External function prototypes @@ -236,27 +257,14 @@ static int lvm_blk_ioctl(struct inode *, struct file *, uint, ulong); static int lvm_blk_open(struct inode *, struct file *); -static int lvm_chr_open(struct inode *, struct file *); - -static int lvm_chr_close(struct inode *, struct file *); static int lvm_blk_close(struct inode *, struct file *); +static int lvm_get_snapshot_use_rate(lv_t *lv_ptr, void *arg); static int lvm_user_bmap(struct inode *, struct lv_bmap *); +static int lvm_chr_open(struct inode *, struct file *); +static int lvm_chr_close(struct inode *, struct file *); static int lvm_chr_ioctl(struct inode *, struct file *, uint, ulong); -int lvm_proc_read_vg_info(char *, char **, off_t, int, int *, void *); -int lvm_proc_read_lv_info(char *, char **, off_t, int, int *, void *); -int lvm_proc_read_pv_info(char *, char **, off_t, int, int *, void *); -static int lvm_proc_get_global_info(char *, char **, off_t, int, int *, void *); - -void lvm_do_create_devfs_entry_of_vg ( vg_t *); - -void lvm_do_create_proc_entry_of_vg ( vg_t *); -void lvm_do_remove_proc_entry_of_vg ( vg_t *); -void lvm_do_create_proc_entry_of_lv ( vg_t *, lv_t *); -void lvm_do_remove_proc_entry_of_lv ( vg_t *, lv_t *); -void lvm_do_create_proc_entry_of_pv ( vg_t *, pv_t *); -void lvm_do_remove_proc_entry_of_pv ( vg_t *, pv_t *); /* End external function prototypes */ @@ -288,33 +296,38 @@ static int lvm_do_pv_change(vg_t*, void*); static int lvm_do_pv_status(vg_t *, void *); +static int lvm_do_pv_flush(void *); -static int lvm_do_vg_create(int, void *); +static int lvm_do_vg_create(void *, int minor); static int lvm_do_vg_extend(vg_t *, void *); static int lvm_do_vg_reduce(vg_t *, void *); static int lvm_do_vg_rename(vg_t *, void *); static int lvm_do_vg_remove(int); static void lvm_geninit(struct gendisk *); -static char *lvm_show_uuid ( char *); +static void __update_hardblocksize(lv_t *lv); + + #ifdef LVM_HD_NAME void lvm_hd_name(char *, int); #endif /* END Internal function prototypes */ -/* volume group descriptor area pointers */ -static vg_t *vg[ABS_MAX_VG]; +/* variables */ +char *lvm_version = "LVM version "LVM_RELEASE_NAME" by Heinz Mauelshagen " + "("LVM_RELEASE_DATE")\n"; +char *lvm_short_version = "version "LVM_RELEASE_NAME" ("LVM_RELEASE_DATE")"; +ushort lvm_iop_version = LVM_DRIVER_IOP_VERSION; +int loadtime = 0; +const char *const lvm_name = LVM_NAME; -static devfs_handle_t lvm_devfs_handle; -static devfs_handle_t vg_devfs_handle[MAX_VG]; -static devfs_handle_t ch_devfs_handle[MAX_VG]; -static devfs_handle_t lv_devfs_handle[MAX_LV]; + +/* volume group descriptor area pointers */ +vg_t *vg[ABS_MAX_VG]; static pv_t *pvp = NULL; static lv_t *lvp = NULL; static pe_t *pep = NULL; -static pe_t *pep1 = NULL; -static char *basename = NULL; /* map from block minor number to VG and LV numbers */ @@ -327,7 +340,6 @@ /* Request structures (lvm_chr_ioctl()) */ static pv_change_req_t pv_change_req; -static pv_flush_req_t pv_flush_req; static pv_status_req_t pv_status_req; static pe_lock_req_t pe_lock_req; static le_remap_req_t le_remap_req; @@ -339,32 +351,24 @@ static char pv_name[NAME_LEN]; /* static char rootvg[NAME_LEN] = { 0, }; */ -const char *const lvm_name = LVM_NAME; static int lock = 0; -static int loadtime = 0; +static int _lock_open_count = 0; static uint vg_count = 0; static long lvm_chr_open_count = 0; -static ushort lvm_iop_version = LVM_DRIVER_IOP_VERSION; static DECLARE_WAIT_QUEUE_HEAD(lvm_wait); static DECLARE_WAIT_QUEUE_HEAD(lvm_map_wait); static spinlock_t lvm_lock = SPIN_LOCK_UNLOCKED; static spinlock_t lvm_snapshot_lock = SPIN_LOCK_UNLOCKED; -static struct proc_dir_entry *lvm_proc_dir = NULL; -static struct proc_dir_entry *lvm_proc_vg_subdir = NULL; -struct proc_dir_entry *pde = NULL; - -static struct file_operations lvm_chr_fops = -{ +struct file_operations lvm_chr_fops = { open: lvm_chr_open, release: lvm_chr_close, ioctl: lvm_chr_ioctl, }; - /* block device operations structure needed for 2.3.38? and above */ -static struct block_device_operations lvm_blk_dops = +struct block_device_operations lvm_blk_dops = { open: lvm_blk_open, release: lvm_blk_close, @@ -374,10 +378,10 @@ /* gendisk structures */ static struct hd_struct lvm_hd_struct[MAX_LV]; -static int lvm_blocksizes[MAX_LV] = -{0,}; -static int lvm_size[MAX_LV] = -{0,}; +static int lvm_blocksizes[MAX_LV]; +static int lvm_hardsectsizes[MAX_LV]; +static int lvm_size[MAX_LV]; + static struct gendisk lvm_gendisk = { MAJOR_NR, /* major # */ @@ -412,18 +416,7 @@ return -EIO; } - lvm_devfs_handle = devfs_register( - 0 , "lvm", 0, 0, LVM_CHAR_MAJOR, - S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP, - &lvm_chr_fops, NULL); - - lvm_proc_dir = create_proc_entry (LVM_DIR, S_IFDIR, &proc_root); - if (lvm_proc_dir != NULL) { - lvm_proc_vg_subdir = create_proc_entry (LVM_VG_SUBDIR, S_IFDIR, lvm_proc_dir); - pde = create_proc_entry(LVM_GLOBAL, S_IFREG, lvm_proc_dir); - if ( pde != NULL) pde->read_proc = &lvm_proc_get_global_info; - } - + lvm_init_fs(); lvm_init_vars(); lvm_geninit(&lvm_gendisk); @@ -467,16 +460,14 @@ return 0; } /* lvm_init() */ - /* * cleanup... */ + static void lvm_cleanup(void) { struct gendisk *gendisk_ptr = NULL, *gendisk_ptr_prev = NULL; - devfs_unregister (lvm_devfs_handle); - if (unregister_chrdev(LVM_CHAR_MAJOR, lvm_name) < 0) { printk(KERN_ERR "%s -- unregister_chrdev failed\n", lvm_name); } @@ -485,6 +476,7 @@ } + gendisk_ptr = gendisk_ptr_prev = gendisk_head; while (gendisk_ptr != NULL) { if (gendisk_ptr == &lvm_gendisk) @@ -500,25 +492,23 @@ blksize_size[MAJOR_NR] = NULL; hardsect_size[MAJOR_NR] = NULL; - remove_proc_entry(LVM_GLOBAL, lvm_proc_dir); - remove_proc_entry(LVM_VG_SUBDIR, lvm_proc_dir); - remove_proc_entry(LVM_DIR, &proc_root); - #ifdef LVM_HD_NAME /* reference from linux/drivers/block/genhd.c */ lvm_hd_name_ptr = NULL; #endif + /* unregister with procfs and devfs */ + lvm_fin_fs(); + printk(KERN_INFO "%s -- Module successfully deactivated\n", lvm_name); return; } /* lvm_cleanup() */ - /* * support function to initialize lvm variables */ -void __init lvm_init_vars(void) +static void __init lvm_init_vars(void) { int v; @@ -554,16 +544,12 @@ /* * character device open routine */ -static int lvm_chr_open(struct inode *inode, - struct file *file) +static int lvm_chr_open(struct inode *inode, struct file *file) { int minor = MINOR(inode->i_rdev); -#ifdef DEBUG - printk(KERN_DEBUG - "%s -- lvm_chr_open MINOR: %d VG#: %d mode: 0x%X lock: %d\n", - lvm_name, minor, VG_CHR(minor), file->f_mode, lock); -#endif + P_DEV("%s -- lvm_chr_open MINOR: %d VG#: %d mode: 0x%X lock: %d\n", + lvm_name, minor, VG_CHR(minor), file->f_mode, lock); /* super user validation */ if (!capable(CAP_SYS_ADMIN)) return -EACCES; @@ -571,6 +557,11 @@ /* Group special file open */ if (VG_CHR(minor) > MAX_VG) return -ENXIO; + spin_lock(&lvm_lock); + if(lock == current->pid) + _lock_open_count++; + spin_unlock(&lvm_lock); + lvm_chr_open_count++; MOD_INC_USE_COUNT; @@ -587,7 +578,7 @@ * */ static int lvm_chr_ioctl(struct inode *inode, struct file *file, - uint command, ulong a) + uint command, ulong a) { int minor = MINOR(inode->i_rdev); uint extendable, l, v; @@ -648,9 +639,13 @@ physical volume (move's done in user space's pvmove) */ return lvm_do_pe_lock_unlock(vg_ptr,arg); - case VG_CREATE: + case VG_CREATE_OLD: /* create a VGDA */ - return lvm_do_vg_create(minor, arg); + return lvm_do_vg_create(arg, minor); + + case VG_CREATE: + /* create a VGDA, assume VG number is filled in */ + return lvm_do_vg_create(arg, -1); case VG_EXTEND: /* extend a volume group */ @@ -701,7 +696,7 @@ case VG_STATUS_GET_NAMELIST: - /* get volume group count */ + /* get volume group names */ for (l = v = 0; v < ABS_MAX_VG; v++) { if (vg[v] != NULL) { if (copy_to_user(arg + l * NAME_LEN, @@ -756,6 +751,7 @@ case LV_STATUS_BYDEV: + /* get status of a logical volume by device */ return lvm_do_lv_status_bydev(vg_ptr, arg); @@ -771,18 +767,12 @@ case PV_FLUSH: /* physical volume buffer flush/invalidate */ - if (copy_from_user(&pv_flush_req, arg, - sizeof(pv_flush_req)) != 0) - return -EFAULT; - - fsync_dev(pv_flush_req.pv_dev); - invalidate_buffers(pv_flush_req.pv_dev); - return 0; + return lvm_do_pv_flush(arg); default: printk(KERN_WARNING - "%s -- lvm_chr_ioctl: unknown command %x\n", + "%s -- lvm_chr_ioctl: unknown command 0x%x\n", lvm_name, command); return -EINVAL; } @@ -794,7 +784,7 @@ /* * character device close routine */ -static int lvm_chr_close(struct inode *inode, struct file *file) +int lvm_chr_close(struct inode *inode, struct file *file) { #ifdef DEBUG int minor = MINOR(inode->i_rdev); @@ -810,10 +800,16 @@ #endif if (lvm_chr_open_count > 0) lvm_chr_open_count--; - if (lock == current->pid) { - lock = 0; /* release lock */ - wake_up_interruptible(&lvm_wait); + + spin_lock(&lvm_lock); + if(lock == current->pid) { + if(!_lock_open_count) { + lock = 0; + wake_up_interruptible(&lvm_wait); + } else + _lock_open_count--; } + spin_unlock(&lvm_lock); MOD_DEC_USE_COUNT; @@ -837,11 +833,8 @@ lv_t *lv_ptr; vg_t *vg_ptr = vg[VG_BLK(minor)]; -#ifdef DEBUG_LVM_BLK_OPEN - printk(KERN_DEBUG - "%s -- lvm_blk_open MINOR: %d VG#: %d LV#: %d mode: 0x%X\n", - lvm_name, minor, VG_BLK(minor), LV_BLK(minor), file->f_mode); -#endif + P_DEV("%s -- lvm_blk_open MINOR: %d VG#: %d LV#: %d mode: 0x%X\n", + lvm_name, minor, VG_BLK(minor), LV_BLK(minor), file->f_mode); #ifdef LVM_TOTAL_RESET if (lvm_reset_spindown > 0) @@ -854,12 +847,16 @@ LV_BLK(minor) >= 0 && LV_BLK(minor) < vg_ptr->lv_max) { - /* Check parallel LV spindown (LV remove) */ + /* Check parallel LV spindown (LV remove) */ if (lv_ptr->lv_status & LV_SPINDOWN) return -EPERM; /* Check inactive LV and open for read/write */ - if (!(lv_ptr->lv_status & LV_ACTIVE)) - return -EPERM; + /* We need to be able to "read" an inactive LV + to re-activate it again */ + if ((file->f_mode & FMODE_WRITE) && + (!(lv_ptr->lv_status & LV_ACTIVE))) + return -EPERM; + if (!(lv_ptr->lv_access & LV_WRITE) && (file->f_mode & FMODE_WRITE)) return -EACCES; @@ -871,12 +868,7 @@ MOD_INC_USE_COUNT; -#ifdef DEBUG_LVM_BLK_OPEN - printk(KERN_DEBUG - "%s -- lvm_blk_open MINOR: %d VG#: %d LV#: %d size: %d\n", - lvm_name, minor, VG_BLK(minor), LV_BLK(minor), - lv_ptr->lv_size); -#endif + P_DEV("%s -- OPEN OK, LV size %d\n", lvm_name, lv_ptr->lv_size); return 0; } @@ -896,7 +888,7 @@ void *arg = (void *) a; struct hd_geometry *hd = (struct hd_geometry *) a; - P_IOCTL("%s -- lvm_blk_ioctl MINOR: %d command: 0x%X arg: %X " + P_IOCTL("%s -- lvm_blk_ioctl MINOR: %d command: 0x%X arg: %lX " "VG#: %dl LV#: %d\n", lvm_name, minor, command, (ulong) arg, VG_BLK(minor), LV_BLK(minor)); @@ -926,8 +918,8 @@ /* set read ahead for block device */ if (!capable(CAP_SYS_ADMIN)) return -EACCES; - P_IOCTL("%s -- lvm_blk_ioctl -- BLKRASET: %d sectors for %02X:%02X\n", - lvm_name, (long) arg, MAJOR(inode->i_rdev), minor); + P_IOCTL("%s -- lvm_blk_ioctl -- BLKRASET: %ld sectors for %s\n", + lvm_name, (long) arg, kdevname(inode->i_rdev)); if ((long) arg < LVM_MIN_READ_AHEAD || (long) arg > LVM_MAX_READ_AHEAD) @@ -964,10 +956,10 @@ copy_to_user((long *) &hd->start, &start, sizeof(start)) != 0) return -EFAULT; - } - P_IOCTL("%s -- lvm_blk_ioctl -- cylinders: %d\n", - lvm_name, lv_ptr->lv_size / heads / sectors); + P_IOCTL("%s -- lvm_blk_ioctl -- cylinders: %d\n", + lvm_name, cylinders); + } break; @@ -991,6 +983,11 @@ break; case LV_BMAP: + /* turn logical block into (dev_t, block). non privileged. */ + /* don't bmap a snapshot, since the mapping can change */ + if(lv_ptr->lv_access & LV_SNAPSHOT) + return -EPERM; + /* turn logical block into (dev_t, block). non privileged. */ return lvm_user_bmap(inode, (struct lv_bmap *) arg); break; @@ -1002,40 +999,11 @@ break; case LV_SNAPSHOT_USE_RATE: - if (!(lv_ptr->lv_access & LV_SNAPSHOT)) return -EPERM; - { - lv_snapshot_use_rate_req_t lv_snapshot_use_rate_req; - - if (copy_from_user(&lv_snapshot_use_rate_req, arg, - sizeof(lv_snapshot_use_rate_req_t))) - return -EFAULT; - if (lv_snapshot_use_rate_req.rate < 0 || - lv_snapshot_use_rate_req.rate > 100) return -EFAULT; - - switch (lv_snapshot_use_rate_req.block) - { - case 0: - lv_ptr->lv_snapshot_use_rate = lv_snapshot_use_rate_req.rate; - if (lv_ptr->lv_remap_ptr * 100 / lv_ptr->lv_remap_end < lv_ptr->lv_snapshot_use_rate) - interruptible_sleep_on (&lv_ptr->lv_snapshot_wait); - break; - - case O_NONBLOCK: - break; - - default: - return -EFAULT; - } - lv_snapshot_use_rate_req.rate = lv_ptr->lv_remap_ptr * 100 / lv_ptr->lv_remap_end; - if (copy_to_user(arg, &lv_snapshot_use_rate_req, - sizeof(lv_snapshot_use_rate_req_t))) - return -EFAULT; - } - break; + return lvm_get_snapshot_use_rate(lv_ptr, arg); default: printk(KERN_WARNING - "%s -- lvm_blk_ioctl: unknown command %d\n", + "%s -- lvm_blk_ioctl: unknown command 0x%x\n", lvm_name, command); return -EINVAL; } @@ -1068,6 +1036,38 @@ return 0; } /* lvm_blk_close() */ +static int lvm_get_snapshot_use_rate(lv_t *lv, void *arg) +{ + lv_snapshot_use_rate_req_t lv_rate_req; + + if (!(lv->lv_access & LV_SNAPSHOT)) + return -EPERM; + + if (copy_from_user(&lv_rate_req, arg, sizeof(lv_rate_req))) + return -EFAULT; + + if (lv_rate_req.rate < 0 || lv_rate_req.rate > 100) + return -EINVAL; + + switch (lv_rate_req.block) { + case 0: + lv->lv_snapshot_use_rate = lv_rate_req.rate; + if (lv->lv_remap_ptr * 100 / lv->lv_remap_end < + lv->lv_snapshot_use_rate) + interruptible_sleep_on(&lv->lv_snapshot_wait); + break; + + case O_NONBLOCK: + break; + + default: + return -EINVAL; + } + lv_rate_req.rate = lv->lv_remap_ptr * 100 / lv->lv_remap_end; + + return copy_to_user(arg, &lv_rate_req, + sizeof(lv_rate_req)) ? -EFAULT : 0; +} static int lvm_user_bmap(struct inode *inode, struct lv_bmap *user_result) { @@ -1079,398 +1079,16 @@ return -EFAULT; memset(&bh,0,sizeof bh); - bh.b_rsector = block; - bh.b_dev = bh.b_rdev = inode->i_dev; + bh.b_blocknr = block; + bh.b_dev = bh.b_rdev = inode->i_rdev; bh.b_size = lvm_get_blksize(bh.b_dev); if ((err=lvm_map(&bh, READ)) < 0) { printk("lvm map failed: %d\n", err); return -EINVAL; } - return put_user(kdev_t_to_nr(bh.b_rdev), &user_result->lv_dev) || - put_user(bh.b_rsector, &user_result->lv_block) ? -EFAULT : 0; -} - - -/* - * provide VG info for proc filesystem use (global) - */ -int lvm_vg_info(vg_t *vg_ptr, char *buf) { - int sz = 0; - char inactive_flag = ' '; - - if (!(vg_ptr->vg_status & VG_ACTIVE)) inactive_flag = 'I'; - sz = sprintf(buf, - "\nVG: %c%s [%d PV, %d LV/%d open] " - " PE Size: %d KB\n" - " Usage [KB/PE]: %d /%d total " - "%d /%d used %d /%d free", - inactive_flag, - vg_ptr->vg_name, - vg_ptr->pv_cur, - vg_ptr->lv_cur, - vg_ptr->lv_open, - vg_ptr->pe_size >> 1, - vg_ptr->pe_size * vg_ptr->pe_total >> 1, - vg_ptr->pe_total, - vg_ptr->pe_allocated * vg_ptr->pe_size >> 1, - vg_ptr->pe_allocated, - (vg_ptr->pe_total - vg_ptr->pe_allocated) * - vg_ptr->pe_size >> 1, - vg_ptr->pe_total - vg_ptr->pe_allocated); - return sz; -} - - -/* - * provide LV info for proc filesystem use (global) - */ -int lvm_lv_info(vg_t *vg_ptr, lv_t *lv_ptr, char *buf) { - int sz = 0; - char inactive_flag = 'A', allocation_flag = ' ', - stripes_flag = ' ', rw_flag = ' '; - - if (!(lv_ptr->lv_status & LV_ACTIVE)) - inactive_flag = 'I'; - rw_flag = 'R'; - if (lv_ptr->lv_access & LV_WRITE) - rw_flag = 'W'; - allocation_flag = 'D'; - if (lv_ptr->lv_allocation & LV_CONTIGUOUS) - allocation_flag = 'C'; - stripes_flag = 'L'; - if (lv_ptr->lv_stripes > 1) - stripes_flag = 'S'; - sz += sprintf(buf+sz, - "[%c%c%c%c", - inactive_flag, - rw_flag, - allocation_flag, - stripes_flag); - if (lv_ptr->lv_stripes > 1) - sz += sprintf(buf+sz, "%-2d", - lv_ptr->lv_stripes); - else - sz += sprintf(buf+sz, " "); - basename = strrchr(lv_ptr->lv_name, '/'); - if ( basename == 0) basename = lv_ptr->lv_name; - else basename++; - sz += sprintf(buf+sz, "] %-25s", basename); - if (strlen(basename) > 25) - sz += sprintf(buf+sz, - "\n "); - sz += sprintf(buf+sz, "%9d /%-6d ", - lv_ptr->lv_size >> 1, - lv_ptr->lv_size / vg_ptr->pe_size); - - if (lv_ptr->lv_open == 0) - sz += sprintf(buf+sz, "close"); - else - sz += sprintf(buf+sz, "%dx open", - lv_ptr->lv_open); - - return sz; -} - - -/* - * provide PV info for proc filesystem use (global) - */ -int lvm_pv_info(pv_t *pv_ptr, char *buf) { - int sz = 0; - char inactive_flag = 'A', allocation_flag = ' '; - char *pv_name = NULL; - - if (!(pv_ptr->pv_status & PV_ACTIVE)) - inactive_flag = 'I'; - allocation_flag = 'A'; - if (!(pv_ptr->pv_allocatable & PV_ALLOCATABLE)) - allocation_flag = 'N'; - pv_name = strrchr(pv_ptr->pv_name+1,'/'); - if ( pv_name == 0) pv_name = pv_ptr->pv_name; - else pv_name++; - sz = sprintf(buf, - "[%c%c] %-21s %8d /%-6d " - "%8d /%-6d %8d /%-6d", - inactive_flag, - allocation_flag, - pv_name, - pv_ptr->pe_total * - pv_ptr->pe_size >> 1, - pv_ptr->pe_total, - pv_ptr->pe_allocated * - pv_ptr->pe_size >> 1, - pv_ptr->pe_allocated, - (pv_ptr->pe_total - - pv_ptr->pe_allocated) * - pv_ptr->pe_size >> 1, - pv_ptr->pe_total - - pv_ptr->pe_allocated); - return sz; -} - - -/* - * Support functions /proc-Filesystem - */ - -#define LVM_PROC_BUF ( i == 0 ? dummy_buf : &buf[sz]) - -/* - * provide global LVM information - */ -static int lvm_proc_get_global_info(char *page, char **start, off_t pos, int count, int *eof, void *data) -{ - int c, i, l, p, v, vg_counter, pv_counter, lv_counter, lv_open_counter, - lv_open_total, pe_t_bytes, hash_table_bytes, lv_block_exception_t_bytes, seconds; - static off_t sz; - off_t sz_last; - static char *buf = NULL; - static char dummy_buf[160]; /* sized for 2 lines */ - vg_t *vg_ptr; - lv_t *lv_ptr; - pv_t *pv_ptr; - - -#ifdef DEBUG_LVM_PROC_GET_INFO - printk(KERN_DEBUG - "%s - lvm_proc_get_global_info CALLED pos: %lu count: %d whence: %d\n", - lvm_name, pos, count, whence); -#endif - - MOD_INC_USE_COUNT; - - if (pos == 0 || buf == NULL) { - sz_last = vg_counter = pv_counter = lv_counter = lv_open_counter = \ - lv_open_total = pe_t_bytes = hash_table_bytes = \ - lv_block_exception_t_bytes = 0; - - /* search for activity */ - for (v = 0; v < ABS_MAX_VG; v++) { - if ((vg_ptr = vg[v]) != NULL) { - vg_counter++; - pv_counter += vg_ptr->pv_cur; - lv_counter += vg_ptr->lv_cur; - if (vg_ptr->lv_cur > 0) { - for (l = 0; l < vg[v]->lv_max; l++) { - if ((lv_ptr = vg_ptr->lv[l]) != NULL) { - pe_t_bytes += lv_ptr->lv_allocated_le; - hash_table_bytes += lv_ptr->lv_snapshot_hash_table_size; - if (lv_ptr->lv_block_exception != NULL) - lv_block_exception_t_bytes += lv_ptr->lv_remap_end; - if (lv_ptr->lv_open > 0) { - lv_open_counter++; - lv_open_total += lv_ptr->lv_open; - } - } - } - } - } - } - pe_t_bytes *= sizeof(pe_t); - lv_block_exception_t_bytes *= sizeof(lv_block_exception_t); - - if (buf != NULL) { - P_KFREE("%s -- vfree %d\n", lvm_name, __LINE__); - lock_kernel(); - vfree(buf); - unlock_kernel(); - buf = NULL; - } - /* 2 times: first to get size to allocate buffer, - 2nd to fill the malloced buffer */ - for (i = 0; i < 2; i++) { - sz = 0; - sz += sprintf(LVM_PROC_BUF, - "LVM " -#ifdef MODULE - "module" -#else - "driver" -#endif - " %s\n\n" - "Total: %d VG%s %d PV%s %d LV%s ", - lvm_short_version, - vg_counter, vg_counter == 1 ? "" : "s", - pv_counter, pv_counter == 1 ? "" : "s", - lv_counter, lv_counter == 1 ? "" : "s"); - sz += sprintf(LVM_PROC_BUF, - "(%d LV%s open", - lv_open_counter, - lv_open_counter == 1 ? "" : "s"); - if (lv_open_total > 0) - sz += sprintf(LVM_PROC_BUF, - " %d times)\n", - lv_open_total); - else - sz += sprintf(LVM_PROC_BUF, ")"); - sz += sprintf(LVM_PROC_BUF, - "\nGlobal: %lu bytes malloced IOP version: %d ", - vg_counter * sizeof(vg_t) + - pv_counter * sizeof(pv_t) + - lv_counter * sizeof(lv_t) + - pe_t_bytes + hash_table_bytes + lv_block_exception_t_bytes + sz_last, - lvm_iop_version); - - seconds = CURRENT_TIME - loadtime; - if (seconds < 0) - loadtime = CURRENT_TIME + seconds; - if (seconds / 86400 > 0) { - sz += sprintf(LVM_PROC_BUF, "%d day%s ", - seconds / 86400, - seconds / 86400 == 0 || - seconds / 86400 > 1 ? "s" : ""); - } - sz += sprintf(LVM_PROC_BUF, "%d:%02d:%02d active\n", - (seconds % 86400) / 3600, - (seconds % 3600) / 60, - seconds % 60); - - if (vg_counter > 0) { - for (v = 0; v < ABS_MAX_VG; v++) { - /* volume group */ - if ((vg_ptr = vg[v]) != NULL) { - sz += lvm_vg_info(vg_ptr, LVM_PROC_BUF); - - /* physical volumes */ - sz += sprintf(LVM_PROC_BUF, - "\n PV%s ", - vg_ptr->pv_cur == 1 ? ": " : "s:"); - c = 0; - for (p = 0; p < vg_ptr->pv_max; p++) { - if ((pv_ptr = vg_ptr->pv[p]) != NULL) { - sz += lvm_pv_info(pv_ptr, LVM_PROC_BUF); - - c++; - if (c < vg_ptr->pv_cur) - sz += sprintf(LVM_PROC_BUF, - "\n "); - } - } - - /* logical volumes */ - sz += sprintf(LVM_PROC_BUF, - "\n LV%s ", - vg_ptr->lv_cur == 1 ? ": " : "s:"); - c = 0; - for (l = 0; l < vg_ptr->lv_max; l++) { - if ((lv_ptr = vg_ptr->lv[l]) != NULL) { - sz += lvm_lv_info(vg_ptr, lv_ptr, LVM_PROC_BUF); - c++; - if (c < vg_ptr->lv_cur) - sz += sprintf(LVM_PROC_BUF, - "\n "); - } - } - if (vg_ptr->lv_cur == 0) sz += sprintf(LVM_PROC_BUF, "none"); - sz += sprintf(LVM_PROC_BUF, "\n"); - } - } - } - if (buf == NULL) { - lock_kernel(); - buf = vmalloc(sz); - unlock_kernel(); - if (buf == NULL) { - sz = 0; - MOD_DEC_USE_COUNT; - return sprintf(page, "%s - vmalloc error at line %d\n", - lvm_name, __LINE__); - } - } - sz_last = sz; - } - } - MOD_DEC_USE_COUNT; - if (pos > sz - 1) { - lock_kernel(); - vfree(buf); - unlock_kernel(); - buf = NULL; - return 0; - } - *start = &buf[pos]; - if (sz - pos < count) - return sz - pos; - else - return count; -} /* lvm_proc_get_global_info() */ - - -/* - * provide VG information - */ -int lvm_proc_read_vg_info(char *page, char **start, off_t off, - int count, int *eof, void *data) { - int sz = 0; - vg_t *vg = data; - - sz += sprintf ( page+sz, "name: %s\n", vg->vg_name); - sz += sprintf ( page+sz, "size: %u\n", - vg->pe_total * vg->pe_size / 2); - sz += sprintf ( page+sz, "access: %u\n", vg->vg_access); - sz += sprintf ( page+sz, "status: %u\n", vg->vg_status); - sz += sprintf ( page+sz, "number: %u\n", vg->vg_number); - sz += sprintf ( page+sz, "LV max: %u\n", vg->lv_max); - sz += sprintf ( page+sz, "LV current: %u\n", vg->lv_cur); - sz += sprintf ( page+sz, "LV open: %u\n", vg->lv_open); - sz += sprintf ( page+sz, "PV max: %u\n", vg->pv_max); - sz += sprintf ( page+sz, "PV current: %u\n", vg->pv_cur); - sz += sprintf ( page+sz, "PV active: %u\n", vg->pv_act); - sz += sprintf ( page+sz, "PE size: %u\n", vg->pe_size / 2); - sz += sprintf ( page+sz, "PE total: %u\n", vg->pe_total); - sz += sprintf ( page+sz, "PE allocated: %u\n", vg->pe_allocated); - sz += sprintf ( page+sz, "uuid: %s\n", lvm_show_uuid(vg->vg_uuid)); - - return sz; -} - - -/* - * provide LV information - */ -int lvm_proc_read_lv_info(char *page, char **start, off_t off, - int count, int *eof, void *data) { - int sz = 0; - lv_t *lv = data; - - sz += sprintf ( page+sz, "name: %s\n", lv->lv_name); - sz += sprintf ( page+sz, "size: %u\n", lv->lv_size); - sz += sprintf ( page+sz, "access: %u\n", lv->lv_access); - sz += sprintf ( page+sz, "status: %u\n", lv->lv_status); - sz += sprintf ( page+sz, "number: %u\n", lv->lv_number); - sz += sprintf ( page+sz, "open: %u\n", lv->lv_open); - sz += sprintf ( page+sz, "allocation: %u\n", lv->lv_allocation); - sz += sprintf ( page+sz, "device: %02u:%02u\n", - MAJOR(lv->lv_dev), MINOR(lv->lv_dev)); - - return sz; -} - - -/* - * provide PV information - */ -int lvm_proc_read_pv_info(char *page, char **start, off_t off, - int count, int *eof, void *data) { - int sz = 0; - pv_t *pv = data; - - sz += sprintf ( page+sz, "name: %s\n", pv->pv_name); - sz += sprintf ( page+sz, "size: %u\n", pv->pv_size); - sz += sprintf ( page+sz, "status: %u\n", pv->pv_status); - sz += sprintf ( page+sz, "number: %u\n", pv->pv_number); - sz += sprintf ( page+sz, "allocatable: %u\n", pv->pv_allocatable); - sz += sprintf ( page+sz, "LV current: %u\n", pv->lv_cur); - sz += sprintf ( page+sz, "PE size: %u\n", pv->pe_size / 2); - sz += sprintf ( page+sz, "PE total: %u\n", pv->pe_total); - sz += sprintf ( page+sz, "PE allocated: %u\n", pv->pe_allocated); - sz += sprintf ( page+sz, "device: %02u:%02u\n", - MAJOR(pv->pv_dev), MINOR(pv->pv_dev)); - sz += sprintf ( page+sz, "uuid: %s\n", lvm_show_uuid(pv->pv_uuid)); - - - return sz; + return (put_user(kdev_t_to_nr(bh.b_rdev), &user_result->lv_dev) || + put_user(bh.b_rsector/(bh.b_size>>9), &user_result->lv_block)); } @@ -1478,172 +1096,151 @@ * block device support function for /usr/src/linux/drivers/block/ll_rw_blk.c * (see init_module/lvm_init) */ +static inline void __remap_snapshot(kdev_t rdev, ulong rsector, + ulong pe_start, lv_t *lv, vg_t *vg) { + if (!lvm_snapshot_remap_block(&rdev, &rsector, pe_start, lv) && + !lvm_snapshot_COW(rdev, rsector, pe_start, rsector, vg, lv)) + lvm_write_COW_table_block(vg, lv); +} + static int lvm_map(struct buffer_head *bh, int rw) { int minor = MINOR(bh->b_dev); - int ret = 0; ulong index; ulong pe_start; ulong size = bh->b_size >> 9; - ulong rsector_tmp = bh->b_blocknr * size; - ulong rsector_sav; - kdev_t rdev_tmp = bh->b_dev; - kdev_t rdev_sav; + ulong rsector_org = bh->b_blocknr * size; + ulong rsector_map; + kdev_t rdev_map; vg_t *vg_this = vg[VG_BLK(minor)]; lv_t *lv = vg_this->lv[LV_BLK(minor)]; + down(&lv->lv_snapshot_sem); if (!(lv->lv_status & LV_ACTIVE)) { printk(KERN_ALERT "%s - lvm_map: ll_rw_blk for inactive LV %s\n", lvm_name, lv->lv_name); - return -1; + goto bad; } if ((rw == WRITE || rw == WRITEA) && !(lv->lv_access & LV_WRITE)) { printk(KERN_CRIT - "%s - lvm_map: ll_rw_blk write for readonly LV %s\n", + "%s - lvm_map: ll_rw_blk write for readonly LV %s\n", lvm_name, lv->lv_name); - return -1; + goto bad; } - P_MAP("%s - lvm_map minor:%d *rdev: %02d:%02d *rsector: %lu " - "size:%lu\n", + P_MAP("%s - lvm_map minor: %d *rdev: %s *rsector: %lu size:%lu\n", lvm_name, minor, - MAJOR(rdev_tmp), - MINOR(rdev_tmp), - rsector_tmp, size); + kdevname(bh->b_dev), + rsector_org, size); - if (rsector_tmp + size > lv->lv_size) { + if (rsector_org + size > lv->lv_size) { printk(KERN_ALERT "%s - lvm_map access beyond end of device; *rsector: " "%lu or size: %lu wrong for minor: %2d\n", - lvm_name, rsector_tmp, size, minor); - return -1; + lvm_name, rsector_org, size, minor); + goto bad; } - rsector_sav = rsector_tmp; - rdev_sav = rdev_tmp; -lvm_second_remap: - /* linear mapping */ - if (lv->lv_stripes < 2) { +remap: + if (lv->lv_stripes < 2) { /* linear mapping */ /* get the index */ - index = rsector_tmp / vg_this->pe_size; + index = rsector_org / vg_this->pe_size; pe_start = lv->lv_current_pe[index].pe; - rsector_tmp = lv->lv_current_pe[index].pe + - (rsector_tmp % vg_this->pe_size); - rdev_tmp = lv->lv_current_pe[index].dev; - - P_MAP("lv_current_pe[%ld].pe: %ld rdev: %02d:%02d " - "rsector:%ld\n", - index, - lv->lv_current_pe[index].pe, - MAJOR(rdev_tmp), - MINOR(rdev_tmp), - rsector_tmp); + rsector_map = lv->lv_current_pe[index].pe + + (rsector_org % vg_this->pe_size); + rdev_map = lv->lv_current_pe[index].dev; + + P_MAP("lv_current_pe[%ld].pe: %d rdev: %s rsector:%ld\n", + index, lv->lv_current_pe[index].pe, + kdevname(rdev_map), rsector_map); - /* striped mapping */ - } else { + } else { /* striped mapping */ ulong stripe_index; ulong stripe_length; stripe_length = vg_this->pe_size * lv->lv_stripes; - stripe_index = (rsector_tmp % stripe_length) / lv->lv_stripesize; - index = rsector_tmp / stripe_length + - (stripe_index % lv->lv_stripes) * - (lv->lv_allocated_le / lv->lv_stripes); + stripe_index = (rsector_org % stripe_length) / + lv->lv_stripesize; + index = rsector_org / stripe_length + + (stripe_index % lv->lv_stripes) * + (lv->lv_allocated_le / lv->lv_stripes); pe_start = lv->lv_current_pe[index].pe; - rsector_tmp = lv->lv_current_pe[index].pe + - (rsector_tmp % stripe_length) - - (stripe_index % lv->lv_stripes) * lv->lv_stripesize - - stripe_index / lv->lv_stripes * - (lv->lv_stripes - 1) * lv->lv_stripesize; - rdev_tmp = lv->lv_current_pe[index].dev; - } - - P_MAP("lv_current_pe[%ld].pe: %ld rdev: %02d:%02d rsector:%ld\n" - "stripe_length: %ld stripe_index: %ld\n", - index, - lv->lv_current_pe[index].pe, - MAJOR(rdev_tmp), - MINOR(rdev_tmp), - rsector_tmp, - stripe_length, - stripe_index); + rsector_map = lv->lv_current_pe[index].pe + + (rsector_org % stripe_length) - + (stripe_index % lv->lv_stripes) * lv->lv_stripesize - + stripe_index / lv->lv_stripes * + (lv->lv_stripes - 1) * lv->lv_stripesize; + rdev_map = lv->lv_current_pe[index].dev; + + P_MAP("lv_current_pe[%ld].pe: %d rdev: %s rsector:%ld\n" + "stripe_length: %ld stripe_index: %ld\n", + index, lv->lv_current_pe[index].pe, kdevname(rdev_map), + rsector_map, stripe_length, stripe_index); + } /* handle physical extents on the move */ if (pe_lock_req.lock == LOCK_PE) { - if (rdev_tmp == pe_lock_req.data.pv_dev && - rsector_tmp >= pe_lock_req.data.pv_offset && - rsector_tmp < (pe_lock_req.data.pv_offset + + if (rdev_map == pe_lock_req.data.pv_dev && + rsector_map >= pe_lock_req.data.pv_offset && + rsector_map < (pe_lock_req.data.pv_offset + vg_this->pe_size)) { sleep_on(&lvm_map_wait); - rsector_tmp = rsector_sav; - rdev_tmp = rdev_sav; - goto lvm_second_remap; + goto remap; } } + /* statistic */ if (rw == WRITE || rw == WRITEA) lv->lv_current_pe[index].writes++; else lv->lv_current_pe[index].reads++; - /* snapshot volume exception handling on physical device address base */ - if (lv->lv_access & (LV_SNAPSHOT|LV_SNAPSHOT_ORG)) { - /* original logical volume */ - if (lv->lv_access & LV_SNAPSHOT_ORG) { - /* Serializes the access to the lv_snapshot_next list */ - down(&lv->lv_snapshot_sem); - if (rw == WRITE || rw == WRITEA) - { - lv_t *lv_ptr; - - /* start with first snapshot and loop thrugh all of them */ - for (lv_ptr = lv->lv_snapshot_next; - lv_ptr != NULL; - lv_ptr = lv_ptr->lv_snapshot_next) { - /* Check for inactive snapshot */ - if (!(lv_ptr->lv_status & LV_ACTIVE)) continue; - /* Serializes the COW with the accesses to the snapshot device */ - down(&lv_ptr->lv_snapshot_sem); - /* do we still have exception storage for this snapshot free? */ - if (lv_ptr->lv_block_exception != NULL) { - rdev_sav = rdev_tmp; - rsector_sav = rsector_tmp; - if (!lvm_snapshot_remap_block(&rdev_tmp, - &rsector_tmp, - pe_start, - lv_ptr)) { - /* create a new mapping */ - if (!(ret = lvm_snapshot_COW(rdev_tmp, - rsector_tmp, - pe_start, - rsector_sav, - lv_ptr))) - ret = lvm_write_COW_table_block(vg_this, - lv_ptr); - } - rdev_tmp = rdev_sav; - rsector_tmp = rsector_sav; - } - up(&lv_ptr->lv_snapshot_sem); - } - } - up(&lv->lv_snapshot_sem); - } else { - /* remap snapshot logical volume */ - down(&lv->lv_snapshot_sem); - if (lv->lv_block_exception != NULL) - lvm_snapshot_remap_block(&rdev_tmp, &rsector_tmp, pe_start, lv); - up(&lv->lv_snapshot_sem); + /* snapshot volume exception handling on physical device + address base */ + if (!(lv->lv_access & (LV_SNAPSHOT|LV_SNAPSHOT_ORG))) + goto out; + + if (lv->lv_access & LV_SNAPSHOT) { /* remap snapshot */ + if (lv->lv_block_exception) + lvm_snapshot_remap_block(&rdev_map, &rsector_map, + pe_start, lv); + else + goto bad; + + } else if(rw == WRITE || rw == WRITEA) { /* snapshot origin */ + lv_t *snap; + + /* start with first snapshot and loop through all of + them */ + for (snap = lv->lv_snapshot_next; snap; + snap = snap->lv_snapshot_next) { + /* Check for inactive snapshot */ + if (!(snap->lv_status & LV_ACTIVE)) + continue; + + /* Serializes the COW with the accesses to the + snapshot device */ + down(&snap->lv_snapshot_sem); + __remap_snapshot(rdev_map, rsector_map, + pe_start, snap, vg_this); + up(&snap->lv_snapshot_sem); } } - bh->b_rdev = rdev_tmp; - bh->b_rsector = rsector_tmp; - return ret; + out: + bh->b_rdev = rdev_map; + bh->b_rsector = rsector_map; + up(&lv->lv_snapshot_sem); + return 1; + + bad: + buffer_IO_error(bh); + up(&lv->lv_snapshot_sem); + return -1; } /* lvm_map() */ @@ -1671,6 +1268,8 @@ #endif + + /* * make request function */ @@ -1780,6 +1379,8 @@ le_remap_req.new_dev; lv_ptr->lv_current_pe[le].pe = le_remap_req.new_pe; + + __update_hardblocksize(lv_ptr); return 0; } } @@ -1793,7 +1394,7 @@ /* * character device support function VGDA create */ -int lvm_do_vg_create(int minor, void *arg) +static int lvm_do_vg_create(void *arg, int minor) { int ret = 0; ulong l, ls = 0, p, size; @@ -1801,8 +1402,6 @@ vg_t *vg_ptr; lv_t **snap_lv_ptr; - if (vg[VG_CHR(minor)] != NULL) return -EPERM; - if ((vg_ptr = kmalloc(sizeof(vg_t),GFP_KERNEL)) == NULL) { printk(KERN_CRIT "%s -- VG_CREATE: kmalloc error VG at line %d\n", @@ -1815,28 +1414,40 @@ return -EFAULT; } + /* VG_CREATE now uses minor number in VG structure */ + if (minor == -1) minor = vg_ptr->vg_number; + + /* Validate it */ + if (vg[VG_CHR(minor)] != NULL) { + kfree(vg_ptr); + return -EPERM; + } + /* we are not that active so far... */ vg_ptr->vg_status &= ~VG_ACTIVE; - vg[VG_CHR(minor)] = vg_ptr; - vg[VG_CHR(minor)]->pe_allocated = 0; + vg_ptr->pe_allocated = 0; if (vg_ptr->pv_max > ABS_MAX_PV) { printk(KERN_WARNING "%s -- Can't activate VG: ABS_MAX_PV too small\n", lvm_name); kfree(vg_ptr); - vg[VG_CHR(minor)] = NULL; return -EPERM; } + if (vg_ptr->lv_max > ABS_MAX_LV) { printk(KERN_WARNING "%s -- Can't activate VG: ABS_MAX_LV too small for %u\n", lvm_name, vg_ptr->lv_max); kfree(vg_ptr); - vg_ptr = NULL; return -EPERM; } + /* create devfs and procfs entries */ + lvm_fs_create_vg(vg_ptr); + + vg[VG_CHR(minor)] = vg_ptr; + /* get the physical volume structures */ vg_ptr->pv_act = vg_ptr->pv_cur = 0; for (p = 0; p < vg_ptr->pv_max; p++) { @@ -1884,8 +1495,6 @@ } } - lvm_do_create_devfs_entry_of_vg ( vg_ptr); - /* Second path to correct snapshot logical volumes which are not in place during first path above */ for (l = 0; l < ls; l++) { @@ -1900,8 +1509,6 @@ } } - lvm_do_create_proc_entry_of_vg ( vg_ptr); - vfree(snap_lv_ptr); vg_count++; @@ -1933,7 +1540,6 @@ if ( ret != 0) return ret; pv_ptr = vg_ptr->pv[p]; vg_ptr->pe_total += pv_ptr->pe_total; - lvm_do_create_proc_entry_of_pv(vg_ptr, pv_ptr); return 0; } } @@ -1983,10 +1589,13 @@ lv_t *lv_ptr = NULL; pv_t *pv_ptr = NULL; + /* If the VG doesn't exist in the kernel then just exit */ + if (!vg_ptr) return 0; + if (copy_from_user(vg_name, arg, sizeof(vg_name)) != 0) return -EFAULT; - lvm_do_remove_proc_entry_of_vg ( vg_ptr); + lvm_fs_remove_vg(vg_ptr); strncpy ( vg_ptr->vg_name, vg_name, sizeof ( vg_name)-1); for ( l = 0; l < vg_ptr->lv_max; l++) @@ -2008,7 +1617,7 @@ strncpy(pv_ptr->vg_name, vg_name, NAME_LEN); } - lvm_do_create_proc_entry_of_vg ( vg_ptr); + lvm_fs_create_vg(vg_ptr); return 0; } /* lvm_do_vg_rename */ @@ -2035,6 +1644,9 @@ /* let's go inactive */ vg_ptr->vg_status &= ~VG_ACTIVE; + /* remove from procfs and devfs */ + lvm_fs_remove_vg(vg_ptr); + /* free LVs */ /* first free snapshot logical volumes */ for (i = 0; i < vg_ptr->lv_max; i++) { @@ -2062,11 +1674,6 @@ } } - devfs_unregister (ch_devfs_handle[vg_ptr->vg_number]); - devfs_unregister (vg_devfs_handle[vg_ptr->vg_number]); - - lvm_do_remove_proc_entry_of_vg ( vg_ptr); - P_KFREE("%s -- kfree %d\n", lvm_name, __LINE__); kfree(vg_ptr); vg[VG_CHR(minor)] = NULL; @@ -2088,7 +1695,7 @@ pv_ptr = vg_ptr->pv[p] = kmalloc(sizeof(pv_t),GFP_KERNEL); if (pv_ptr == NULL) { printk(KERN_CRIT - "%s -- VG_CREATE: kmalloc error PV at line %d\n", + "%s -- PV_CREATE: kmalloc error PV at line %d\n", lvm_name, __LINE__); return -ENOMEM; } @@ -2102,18 +1709,20 @@ pv_ptr->pv_status = PV_ACTIVE; vg_ptr->pv_act++; vg_ptr->pv_cur++; + lvm_fs_create_pv(vg_ptr, pv_ptr); return 0; } /* lvm_do_pv_create() */ /* - * character device support function physical volume create + * character device support function physical volume remove */ static int lvm_do_pv_remove(vg_t *vg_ptr, ulong p) { pv_t *pv_ptr = vg_ptr->pv[p]; - lvm_do_remove_proc_entry_of_pv ( vg_ptr, pv_ptr); + lvm_fs_remove_pv(vg_ptr, pv_ptr); + vg_ptr->pe_total -= pv_ptr->pe_total; vg_ptr->pv_cur--; vg_ptr->pv_act--; @@ -2127,12 +1736,39 @@ } +static void __update_hardblocksize(lv_t *lv) { + int le, e; + int max_hardblocksize = 0, hardblocksize; + + for (le = 0; le < lv->lv_allocated_le; le++) { + hardblocksize = get_hardblocksize(lv->lv_current_pe[le].dev); + if (hardblocksize == 0) + hardblocksize = 512; + if (hardblocksize > max_hardblocksize) + max_hardblocksize = hardblocksize; + } + + if (lv->lv_access & LV_SNAPSHOT) { + for (e = 0; e < lv->lv_remap_end; e++) { + hardblocksize = + get_hardblocksize( + lv->lv_block_exception[e].rdev_new); + if (hardblocksize == 0) + hardblocksize = 512; + if (hardblocksize > max_hardblocksize) + max_hardblocksize = hardblocksize; + } + } + + lvm_hardsectsizes[MINOR(lv->lv_dev)] = max_hardblocksize; +} + /* * character device support function logical volume create */ static int lvm_do_lv_create(int minor, char *lv_name, lv_t *lv) { - int e, ret, l, le, l_new, p, size; + int e, ret, l, le, l_new, p, size, activate = 1; ulong lv_status_save; lv_block_exception_t *lvbe = lv->lv_block_exception; vg_t *vg_ptr = vg[VG_CHR(minor)]; @@ -2142,7 +1778,7 @@ if (lv->lv_chunk_size > LVM_SNAPSHOT_MAX_CHUNK) return -EINVAL; - for (l = 0; l < vg_ptr->lv_max; l++) { + for (l = 0; l < vg_ptr->lv_cur; l++) { if (vg_ptr->lv[l] != NULL && strcmp(vg_ptr->lv[l]->lv_name, lv_name) == 0) return -EEXIST; @@ -2176,16 +1812,17 @@ lv_ptr->lv_snapshot_next = NULL; lv_ptr->lv_block_exception = NULL; lv_ptr->lv_iobuf = NULL; + lv_ptr->lv_COW_table_iobuf = NULL; lv_ptr->lv_snapshot_hash_table = NULL; lv_ptr->lv_snapshot_hash_table_size = 0; lv_ptr->lv_snapshot_hash_mask = 0; - lv_ptr->lv_COW_table_page = NULL; init_MUTEX(&lv_ptr->lv_snapshot_sem); lv_ptr->lv_snapshot_use_rate = 0; + vg_ptr->lv[l] = lv_ptr; /* get the PE structures from user space if this - is no snapshot logical volume */ + is not a snapshot logical volume */ if (!(lv_ptr->lv_access & LV_SNAPSHOT)) { size = lv_ptr->lv_allocated_le * sizeof(pe_t); if ((lv_ptr->lv_current_pe = vmalloc(size)) == NULL) { @@ -2237,6 +1874,16 @@ vg_ptr->lv[l] = NULL; return -EFAULT; } + + if(lv_ptr->lv_block_exception[0].rsector_org == + LVM_SNAPSHOT_DROPPED_SECTOR) + { + printk(KERN_WARNING + "%s -- lvm_do_lv_create: snapshot has been dropped and will not be activated\n", + lvm_name); + activate = 0; + } + /* point to the original logical volume */ lv_ptr = lv_ptr->lv_snapshot_org; @@ -2270,10 +1917,13 @@ lv_ptr->lv_block_exception[e].rsector_org, lv_ptr); /* need to fill the COW exception table data into the page for disk i/o */ - lvm_snapshot_fill_COW_page(vg_ptr, lv_ptr); + if(lvm_snapshot_fill_COW_page(vg_ptr, lv_ptr)) { + kfree(lv_ptr); + vg_ptr->lv[l] = NULL; + return -EINVAL; + } init_waitqueue_head(&lv_ptr->lv_snapshot_wait); } else { - vfree(lv_ptr->lv_block_exception); kfree(lv_ptr); vg_ptr->lv[l] = NULL; return -EFAULT; @@ -2295,21 +1945,7 @@ vg_ptr->lv_cur++; lv_ptr->lv_status = lv_status_save; - { - char *lv_tmp, *lv_buf = lv->lv_name; - - strtok(lv->lv_name, "/"); /* /dev */ - while((lv_tmp = strtok(NULL, "/")) != NULL) - lv_buf = lv_tmp; - - lv_devfs_handle[lv->lv_number] = devfs_register( - vg_devfs_handle[vg_ptr->vg_number], lv_buf, - DEVFS_FL_DEFAULT, LVM_BLK_MAJOR, lv->lv_number, - S_IFBLK | S_IRUSR | S_IWUSR | S_IRGRP, - &lvm_blk_dops, NULL); - } - - lvm_do_create_proc_entry_of_lv ( vg_ptr, lv_ptr); + __update_hardblocksize(lv_ptr); /* optionally add our new snapshot LV */ if (lv_ptr->lv_access & LV_SNAPSHOT) { @@ -2326,6 +1962,7 @@ org->lv_access |= LV_SNAPSHOT_ORG; lv_ptr->lv_access &= ~LV_SNAPSHOT_ORG; /* this can only hide an userspace bug */ + /* Link in the list of snapshot volumes */ for (last = org; last->lv_snapshot_next; last = last->lv_snapshot_next); lv_ptr->lv_snapshot_prev = last; @@ -2334,7 +1971,11 @@ } /* activate the logical volume */ - lv_ptr->lv_status |= LV_ACTIVE; + if(activate) + lv_ptr->lv_status |= LV_ACTIVE; + else + lv_ptr->lv_status &= ~LV_ACTIVE; + if ( lv_ptr->lv_access & LV_WRITE) set_device_ro(lv_ptr->lv_dev, 0); else @@ -2349,6 +1990,7 @@ lv_ptr->vg = vg_ptr; + lvm_fs_create_lv(vg_ptr, lv_ptr); return 0; } /* lvm_do_lv_create() */ @@ -2386,6 +2028,8 @@ lv_ptr->lv_snapshot_next != NULL) return -EPERM; + lvm_fs_remove_lv(vg_ptr, lv_ptr); + if (lv_ptr->lv_access & LV_SNAPSHOT) { /* * Atomically make the the snapshot invisible @@ -2400,11 +2044,13 @@ lv_ptr->lv_snapshot_next->lv_snapshot_prev = lv_ptr->lv_snapshot_prev; } - up(&org->lv_snapshot_sem); /* no more snapshots? */ - if (!org->lv_snapshot_next) + if (!org->lv_snapshot_next) { org->lv_access &= ~LV_SNAPSHOT_ORG; + } + up(&org->lv_snapshot_sem); + lvm_snapshot_release(lv_ptr); /* Update the VG PE(s) used by snapshot reserve space. */ @@ -2447,10 +2093,6 @@ vfree(lv_ptr->lv_current_pe); } - devfs_unregister(lv_devfs_handle[lv_ptr->lv_number]); - - lvm_do_remove_proc_entry_of_lv ( vg_ptr, lv_ptr); - P_KFREE("%s -- kfree %d\n", lvm_name, __LINE__); kfree(lv_ptr); vg_ptr->lv[l] = NULL; @@ -2460,204 +2102,212 @@ /* - * character device support function logical volume extend / reduce + * logical volume extend / reduce */ -static int lvm_do_lv_extend_reduce(int minor, char *lv_name, lv_t *lv) -{ - ulong end, l, le, p, size, old_allocated_le; - vg_t *vg_ptr = vg[VG_CHR(minor)]; - lv_t *lv_ptr; - pe_t *pe; +static int __extend_reduce_snapshot(vg_t *vg_ptr, lv_t *old_lv, lv_t *new_lv) { + ulong size; + lv_block_exception_t *lvbe; - if ((pep = lv->lv_current_pe) == NULL) return -EINVAL; + if (!new_lv->lv_block_exception) + return -ENXIO; - for (l = 0; l < vg_ptr->lv_max; l++) { - if (vg_ptr->lv[l] != NULL && - strcmp(vg_ptr->lv[l]->lv_name, lv_name) == 0) - break; + size = new_lv->lv_remap_end * sizeof(lv_block_exception_t); + if ((lvbe = vmalloc(size)) == NULL) { + printk(KERN_CRIT + "%s -- lvm_do_lv_extend_reduce: vmalloc " + "error LV_BLOCK_EXCEPTION of %lu Byte at line %d\n", + lvm_name, size, __LINE__); + return -ENOMEM; } - if (l == vg_ptr->lv_max) return -ENXIO; - lv_ptr = vg_ptr->lv[l]; - /* check for active snapshot */ - if (lv->lv_access & LV_SNAPSHOT) - { - ulong e; - lv_block_exception_t *lvbe, *lvbe_old; - struct list_head * lvs_hash_table_old; - - if (lv->lv_block_exception == NULL) return -ENXIO; - size = lv->lv_remap_end * sizeof ( lv_block_exception_t); - if ((lvbe = vmalloc(size)) == NULL) - { - printk(KERN_CRIT - "%s -- lvm_do_lv_extend_reduce: vmalloc error LV_BLOCK_EXCEPTION " - "of %lu Byte at line %d\n", - lvm_name, size, __LINE__); - return -ENOMEM; - } - if (lv->lv_remap_end > lv_ptr->lv_remap_end) - { - if (copy_from_user(lvbe, lv->lv_block_exception, size)) - { - vfree(lvbe); - return -EFAULT; - } - } - - lvbe_old = lv_ptr->lv_block_exception; - lvs_hash_table_old = lv_ptr->lv_snapshot_hash_table; - - /* we need to play on the safe side here... */ - down(&lv_ptr->lv_snapshot_org->lv_snapshot_sem); - if (lv_ptr->lv_block_exception == NULL || - lv_ptr->lv_remap_ptr > lv_ptr->lv_remap_end) - { - up(&lv_ptr->lv_snapshot_org->lv_snapshot_sem); - vfree(lvbe); - return -EPERM; - } - memcpy(lvbe, - lv_ptr->lv_block_exception, - (lv->lv_remap_end > lv_ptr->lv_remap_end ? - lv_ptr->lv_remap_ptr : lv->lv_remap_end) * sizeof(lv_block_exception_t)); - - lv_ptr->lv_block_exception = lvbe; - lv_ptr->lv_remap_end = lv->lv_remap_end; - if (lvm_snapshot_alloc_hash_table(lv_ptr) != 0) - { - lvm_drop_snapshot(lv_ptr, "no memory for hash table"); - up(&lv_ptr->lv_snapshot_org->lv_snapshot_sem); - vfree(lvbe_old); - vfree(lvs_hash_table_old); - return -ENOMEM; - } - - for (e = 0; e < lv_ptr->lv_remap_ptr; e++) - lvm_hash_link (lv_ptr->lv_block_exception + e, - lv_ptr->lv_block_exception[e].rdev_org, - lv_ptr->lv_block_exception[e].rsector_org, lv_ptr); - - up(&lv_ptr->lv_snapshot_org->lv_snapshot_sem); - - vfree(lvbe_old); - vfree(lvs_hash_table_old); + if ((new_lv->lv_remap_end > old_lv->lv_remap_end) && + (copy_from_user(lvbe, new_lv->lv_block_exception, size))) { + vfree(lvbe); + return -EFAULT; + } + new_lv->lv_block_exception = lvbe; - return 0; + if (lvm_snapshot_alloc_hash_table(new_lv)) { + vfree(new_lv->lv_block_exception); + return -ENOMEM; } + return 0; +} - /* we drop in here in case it is an original logical volume */ - if ((pe = vmalloc(size = lv->lv_current_le * sizeof(pe_t))) == NULL) { +static int __extend_reduce(vg_t *vg_ptr, lv_t *old_lv, lv_t *new_lv) { + ulong size, l, p, end; + pe_t *pe; + + /* allocate space for new pe structures */ + size = new_lv->lv_current_le * sizeof(pe_t); + if ((pe = vmalloc(size)) == NULL) { printk(KERN_CRIT - "%s -- lvm_do_lv_extend_reduce: vmalloc error LV_CURRENT_PE " - "of %lu Byte at line %d\n", + "%s -- lvm_do_lv_extend_reduce: " + "vmalloc error LV_CURRENT_PE of %lu Byte at line %d\n", lvm_name, size, __LINE__); return -ENOMEM; } + /* get the PE structures from user space */ - if (copy_from_user(pe, pep, size)) { + if (copy_from_user(pe, new_lv->lv_current_pe, size)) { + if(old_lv->lv_access & LV_SNAPSHOT) + vfree(new_lv->lv_snapshot_hash_table); vfree(pe); return -EFAULT; } + new_lv->lv_current_pe = pe; + /* reduce allocation counters on PV(s) */ - for (le = 0; le < lv_ptr->lv_allocated_le; le++) { + for (l = 0; l < old_lv->lv_allocated_le; l++) { vg_ptr->pe_allocated--; for (p = 0; p < vg_ptr->pv_cur; p++) { if (vg_ptr->pv[p]->pv_dev == - lv_ptr->lv_current_pe[le].dev) { + old_lv->lv_current_pe[l].dev) { vg_ptr->pv[p]->pe_allocated--; break; } } } - - /* save pointer to "old" lv/pe pointer array */ - pep1 = lv_ptr->lv_current_pe; - end = lv_ptr->lv_current_le; - - /* save open counter... */ - lv->lv_open = lv_ptr->lv_open; - lv->lv_snapshot_prev = lv_ptr->lv_snapshot_prev; - lv->lv_snapshot_next = lv_ptr->lv_snapshot_next; - lv->lv_snapshot_org = lv_ptr->lv_snapshot_org; - - lv->lv_current_pe = pe; - - /* save # of old allocated logical extents */ - old_allocated_le = lv_ptr->lv_allocated_le; - - /* copy preloaded LV */ - memcpy((char *) lv_ptr, (char *) lv, sizeof(lv_t)); - - lvm_gendisk.part[MINOR(lv_ptr->lv_dev)].start_sect = 0; - lvm_gendisk.part[MINOR(lv_ptr->lv_dev)].nr_sects = lv_ptr->lv_size; - lvm_size[MINOR(lv_ptr->lv_dev)] = lv_ptr->lv_size >> 1; - /* vg_lv_map array doesn't have to be changed here */ - - LVM_CORRECT_READ_AHEAD(lv_ptr->lv_read_ahead); + /* extend the PE count in PVs */ + for (l = 0; l < new_lv->lv_allocated_le; l++) { + vg_ptr->pe_allocated++; + for (p = 0; p < vg_ptr->pv_cur; p++) { + if (vg_ptr->pv[p]->pv_dev == + new_lv->lv_current_pe[l].dev) { + vg_ptr->pv[p]->pe_allocated++; + break; + } + } + } /* save availiable i/o statistic data */ - /* linear logical volume */ - if (lv_ptr->lv_stripes < 2) { - /* Check what last LE shall be used */ - if (end > lv_ptr->lv_current_le) end = lv_ptr->lv_current_le; - for (le = 0; le < end; le++) { - lv_ptr->lv_current_pe[le].reads += pep1[le].reads; - lv_ptr->lv_current_pe[le].writes += pep1[le].writes; + if (old_lv->lv_stripes < 2) { /* linear logical volume */ + end = min(old_lv->lv_current_le, new_lv->lv_current_le); + for (l = 0; l < end; l++) { + new_lv->lv_current_pe[l].reads += + old_lv->lv_current_pe[l].reads; + + new_lv->lv_current_pe[l].writes += + old_lv->lv_current_pe[l].writes; } - /* striped logical volume */ - } else { + + } else { /* striped logical volume */ uint i, j, source, dest, end, old_stripe_size, new_stripe_size; - old_stripe_size = old_allocated_le / lv_ptr->lv_stripes; - new_stripe_size = lv_ptr->lv_allocated_le / lv_ptr->lv_stripes; - end = old_stripe_size; - if (end > new_stripe_size) end = new_stripe_size; + old_stripe_size = old_lv->lv_allocated_le / old_lv->lv_stripes; + new_stripe_size = new_lv->lv_allocated_le / new_lv->lv_stripes; + end = min(old_stripe_size, new_stripe_size); + for (i = source = dest = 0; - i < lv_ptr->lv_stripes; i++) { + i < new_lv->lv_stripes; i++) { for (j = 0; j < end; j++) { - lv_ptr->lv_current_pe[dest + j].reads += - pep1[source + j].reads; - lv_ptr->lv_current_pe[dest + j].writes += - pep1[source + j].writes; + new_lv->lv_current_pe[dest + j].reads += + old_lv->lv_current_pe[source + j].reads; + new_lv->lv_current_pe[dest + j].writes += + old_lv->lv_current_pe[source + j].writes; } source += old_stripe_size; dest += new_stripe_size; } } - /* extend the PE count in PVs */ - for (le = 0; le < lv_ptr->lv_allocated_le; le++) { - vg_ptr->pe_allocated++; - for (p = 0; p < vg_ptr->pv_cur; p++) { - if (vg_ptr->pv[p]->pv_dev == - lv_ptr->lv_current_pe[le].dev) { - vg_ptr->pv[p]->pe_allocated++; - break; - } - } - } + return 0; +} + +static int lvm_do_lv_extend_reduce(int minor, char *lv_name, lv_t *new_lv) +{ + int r; + ulong l, e, size; + vg_t *vg_ptr = vg[VG_CHR(minor)]; + lv_t *old_lv; + pe_t *pe; - vfree ( pep1); - pep1 = NULL; + if ((pe = new_lv->lv_current_pe) == NULL) + return -EINVAL; - if (lv->lv_access & LV_SNAPSHOT_ORG) - { - /* Correct the snapshot size information */ - while ((lv_ptr = lv_ptr->lv_snapshot_next) != NULL) - { - lv_ptr->lv_current_pe = lv_ptr->lv_snapshot_org->lv_current_pe; - lv_ptr->lv_allocated_le = lv_ptr->lv_snapshot_org->lv_allocated_le; - lv_ptr->lv_current_le = lv_ptr->lv_snapshot_org->lv_current_le; - lv_ptr->lv_size = lv_ptr->lv_snapshot_org->lv_size; - lvm_gendisk.part[MINOR(lv_ptr->lv_dev)].nr_sects = lv_ptr->lv_size; - lvm_size[MINOR(lv_ptr->lv_dev)] = lv_ptr->lv_size >> 1; + for (l = 0; l < vg_ptr->lv_max; l++) + if (vg_ptr->lv[l] && !strcmp(vg_ptr->lv[l]->lv_name, lv_name)) + break; + + if (l == vg_ptr->lv_max) + return -ENXIO; + + old_lv = vg_ptr->lv[l]; + + if (old_lv->lv_access & LV_SNAPSHOT) + r = __extend_reduce_snapshot(vg_ptr, old_lv, new_lv); + else + r = __extend_reduce(vg_ptr, old_lv, new_lv); + + if(r) + return r; + + /* copy relevent fields */ + down(&old_lv->lv_snapshot_sem); + + if(new_lv->lv_access & LV_SNAPSHOT) { + + size = (new_lv->lv_remap_end > old_lv->lv_remap_end) ? + old_lv->lv_remap_ptr : new_lv->lv_remap_end; + size *= sizeof(lv_block_exception_t); + memcpy(new_lv->lv_block_exception, + old_lv->lv_block_exception, size); + + old_lv->lv_remap_end = new_lv->lv_remap_end; + old_lv->lv_block_exception = new_lv->lv_block_exception; + old_lv->lv_snapshot_hash_table = + new_lv->lv_snapshot_hash_table; + old_lv->lv_snapshot_hash_table_size = + new_lv->lv_snapshot_hash_table_size; + old_lv->lv_snapshot_hash_mask = + new_lv->lv_snapshot_hash_mask; + + for (e = 0; e < new_lv->lv_remap_ptr; e++) + lvm_hash_link(new_lv->lv_block_exception + e, + new_lv->lv_block_exception[e].rdev_org, + new_lv->lv_block_exception[e].rsector_org, + new_lv); + + } else { + + vfree(old_lv->lv_current_pe); + vfree(old_lv->lv_snapshot_hash_table); + + old_lv->lv_size = new_lv->lv_size; + old_lv->lv_allocated_le = new_lv->lv_allocated_le; + old_lv->lv_current_le = new_lv->lv_current_le; + old_lv->lv_current_pe = new_lv->lv_current_pe; + lvm_gendisk.part[MINOR(old_lv->lv_dev)].nr_sects = + old_lv->lv_size; + lvm_size[MINOR(old_lv->lv_dev)] = old_lv->lv_size >> 1; + + if (old_lv->lv_access & LV_SNAPSHOT_ORG) { + lv_t *snap; + for(snap = old_lv->lv_snapshot_next; snap; + snap = snap->lv_snapshot_next) { + down(&snap->lv_snapshot_sem); + snap->lv_current_pe = old_lv->lv_current_pe; + snap->lv_allocated_le = + old_lv->lv_allocated_le; + snap->lv_current_le = old_lv->lv_current_le; + snap->lv_size = old_lv->lv_size; + + lvm_gendisk.part[MINOR(snap->lv_dev)].nr_sects + = old_lv->lv_size; + lvm_size[MINOR(snap->lv_dev)] = + old_lv->lv_size >> 1; + __update_hardblocksize(snap); + up(&snap->lv_snapshot_sem); + } } } + __update_hardblocksize(old_lv); + up(&old_lv->lv_snapshot_sem); + return 0; } /* lvm_do_lv_extend_reduce() */ @@ -2668,10 +2318,10 @@ static int lvm_do_lv_status_byname(vg_t *vg_ptr, void *arg) { uint l; - ulong size; - lv_t lv; - lv_t *lv_ptr; lv_status_byname_req_t lv_status_byname_req; + void *saved_ptr1; + void *saved_ptr2; + lv_t *lv_ptr; if (vg_ptr == NULL) return -ENXIO; if (copy_from_user(&lv_status_byname_req, arg, @@ -2679,28 +2329,27 @@ return -EFAULT; if (lv_status_byname_req.lv == NULL) return -EINVAL; - if (copy_from_user(&lv, lv_status_byname_req.lv, - sizeof(lv_t)) != 0) - return -EFAULT; for (l = 0; l < vg_ptr->lv_max; l++) { - lv_ptr = vg_ptr->lv[l]; - if (lv_ptr != NULL && + if ((lv_ptr = vg_ptr->lv[l]) != NULL && strcmp(lv_ptr->lv_name, - lv_status_byname_req.lv_name) == 0) { - if (copy_to_user(lv_status_byname_req.lv, + lv_status_byname_req.lv_name) == 0) { + /* Save usermode pointers */ + saved_ptr1 = lv_status_byname_req.lv->lv_current_pe; + saved_ptr2 = lv_status_byname_req.lv->lv_block_exception; + if (copy_to_user(lv_status_byname_req.lv, lv_ptr, sizeof(lv_t)) != 0) return -EFAULT; - - if (lv.lv_current_pe != NULL) { - size = lv_ptr->lv_allocated_le * - sizeof(pe_t); - if (copy_to_user(lv.lv_current_pe, + if (saved_ptr1 != NULL) { + if (copy_to_user(saved_ptr1, lv_ptr->lv_current_pe, - size) != 0) + lv_ptr->lv_allocated_le * + sizeof(pe_t)) != 0) return -EFAULT; } + /* Restore usermode pointers */ + lv_status_byname_req.lv->lv_current_pe = saved_ptr1; return 0; } } @@ -2713,34 +2362,37 @@ */ static int lvm_do_lv_status_byindex(vg_t *vg_ptr,void *arg) { - ulong size; - lv_t lv; - lv_t *lv_ptr; lv_status_byindex_req_t lv_status_byindex_req; + void *saved_ptr1; + void *saved_ptr2; + lv_t *lv_ptr; if (vg_ptr == NULL) return -ENXIO; if (copy_from_user(&lv_status_byindex_req, arg, sizeof(lv_status_byindex_req)) != 0) return -EFAULT; - if ((lvp = lv_status_byindex_req.lv) == NULL) + if (lv_status_byindex_req.lv == NULL) return -EINVAL; if ( ( lv_ptr = vg_ptr->lv[lv_status_byindex_req.lv_index]) == NULL) return -ENXIO; - if (copy_from_user(&lv, lvp, sizeof(lv_t)) != 0) - return -EFAULT; - - if (copy_to_user(lvp, lv_ptr, sizeof(lv_t)) != 0) + /* Save usermode pointers */ + saved_ptr1 = lv_status_byindex_req.lv->lv_current_pe; + saved_ptr2 = lv_status_byindex_req.lv->lv_block_exception; + if (copy_to_user(lv_status_byindex_req.lv, lv_ptr, sizeof(lv_t)) != 0) return -EFAULT; - - if (lv.lv_current_pe != NULL) { - size = lv_ptr->lv_allocated_le * sizeof(pe_t); - if (copy_to_user(lv.lv_current_pe, - lv_ptr->lv_current_pe, - size) != 0) + if (saved_ptr1 != NULL) { + if (copy_to_user(saved_ptr1, + lv_ptr->lv_current_pe, + lv_ptr->lv_allocated_le * + sizeof(pe_t)) != 0) return -EFAULT; } + + /* Restore usermode pointers */ + lv_status_byindex_req.lv->lv_current_pe = saved_ptr1; + return 0; } /* lvm_do_lv_status_byindex() */ @@ -2751,6 +2403,9 @@ static int lvm_do_lv_status_bydev(vg_t * vg_ptr, void * arg) { int l; lv_status_bydev_req_t lv_status_bydev_req; + void *saved_ptr1; + void *saved_ptr2; + lv_t *lv_ptr; if (vg_ptr == NULL) return -ENXIO; if (copy_from_user(&lv_status_bydev_req, arg, @@ -2763,10 +2418,23 @@ } if ( l == vg_ptr->lv_max) return -ENXIO; + lv_ptr = vg_ptr->lv[l]; + + /* Save usermode pointers */ + saved_ptr1 = lv_status_bydev_req.lv->lv_current_pe; + saved_ptr2 = lv_status_bydev_req.lv->lv_block_exception; - if (copy_to_user(lv_status_bydev_req.lv, - vg_ptr->lv[l], sizeof(lv_t)) != 0) + if (copy_to_user(lv_status_bydev_req.lv, lv_ptr, sizeof(lv_t)) != 0) return -EFAULT; + if (saved_ptr1 != NULL) { + if (copy_to_user(saved_ptr1, + lv_ptr->lv_current_pe, + lv_ptr->lv_allocated_le * + sizeof(pe_t)) != 0) + return -EFAULT; + } + /* Restore usermode pointers */ + lv_status_bydev_req.lv->lv_current_pe = saved_ptr1; return 0; } /* lvm_do_lv_status_bydev() */ @@ -2786,11 +2454,11 @@ if ( (lv_ptr = vg_ptr->lv[l]) == NULL) continue; if (lv_ptr->lv_dev == lv->lv_dev) { - lvm_do_remove_proc_entry_of_lv ( vg_ptr, lv_ptr); + lvm_fs_remove_lv(vg_ptr, lv_ptr); strncpy(lv_ptr->lv_name, lv_req->lv_name, NAME_LEN); - lvm_do_create_proc_entry_of_lv ( vg_ptr, lv_ptr); + lvm_fs_create_lv(vg_ptr, lv_ptr); break; } } @@ -2870,160 +2538,28 @@ } /* lvm_do_pv_status() */ - /* - * create a devfs entry for a volume group + * character device support function flush and invalidate all buffers of a PV */ -void lvm_do_create_devfs_entry_of_vg ( vg_t *vg_ptr) { - vg_devfs_handle[vg_ptr->vg_number] = devfs_mk_dir(0, vg_ptr->vg_name, NULL); - ch_devfs_handle[vg_ptr->vg_number] = devfs_register( - vg_devfs_handle[vg_ptr->vg_number] , "group", - DEVFS_FL_DEFAULT, LVM_CHAR_MAJOR, vg_ptr->vg_number, - S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP, - &lvm_chr_fops, NULL); -} - - -/* - * create a /proc entry for a logical volume - */ -void lvm_do_create_proc_entry_of_lv ( vg_t *vg_ptr, lv_t *lv_ptr) { - char *basename; - - if ( vg_ptr->lv_subdir_pde != NULL) { - basename = strrchr(lv_ptr->lv_name, '/'); - if (basename == NULL) basename = lv_ptr->lv_name; - else basename++; - pde = create_proc_entry(basename, S_IFREG, - vg_ptr->lv_subdir_pde); - if ( pde != NULL) { - pde->read_proc = lvm_proc_read_lv_info; - pde->data = lv_ptr; - } - } -} - - -/* - * remove a /proc entry for a logical volume - */ -void lvm_do_remove_proc_entry_of_lv ( vg_t *vg_ptr, lv_t *lv_ptr) { - char *basename; - - if ( vg_ptr->lv_subdir_pde != NULL) { - basename = strrchr(lv_ptr->lv_name, '/'); - if (basename == NULL) basename = lv_ptr->lv_name; - else basename++; - remove_proc_entry(basename, vg_ptr->lv_subdir_pde); - } -} - - -/* - * create a /proc entry for a physical volume - */ -void lvm_do_create_proc_entry_of_pv ( vg_t *vg_ptr, pv_t *pv_ptr) { - int offset = 0; - char *basename; - char buffer[NAME_LEN]; - - basename = pv_ptr->pv_name; - if (strncmp(basename, "/dev/", 5) == 0) offset = 5; - strncpy(buffer, basename + offset, sizeof(buffer)); - basename = buffer; - while ( ( basename = strchr ( basename, '/')) != NULL) *basename = '_'; - pde = create_proc_entry(buffer, S_IFREG, vg_ptr->pv_subdir_pde); - if ( pde != NULL) { - pde->read_proc = lvm_proc_read_pv_info; - pde->data = pv_ptr; - } -} - - -/* - * remove a /proc entry for a physical volume - */ -void lvm_do_remove_proc_entry_of_pv ( vg_t *vg_ptr, pv_t *pv_ptr) { - char *basename; - - basename = strrchr(pv_ptr->pv_name, '/'); - if ( vg_ptr->pv_subdir_pde != NULL) { - basename = strrchr(pv_ptr->pv_name, '/'); - if (basename == NULL) basename = pv_ptr->pv_name; - else basename++; - remove_proc_entry(basename, vg_ptr->pv_subdir_pde); - } -} - - -/* - * create a /proc entry for a volume group - */ -void lvm_do_create_proc_entry_of_vg ( vg_t *vg_ptr) { - int l, p; - pv_t *pv_ptr; - lv_t *lv_ptr; +static int lvm_do_pv_flush(void *arg) +{ + pv_flush_req_t pv_flush_req; - pde = create_proc_entry(vg_ptr->vg_name, S_IFDIR, - lvm_proc_vg_subdir); - if ( pde != NULL) { - vg_ptr->vg_dir_pde = pde; - pde = create_proc_entry("group", S_IFREG, - vg_ptr->vg_dir_pde); - if ( pde != NULL) { - pde->read_proc = lvm_proc_read_vg_info; - pde->data = vg_ptr; - } - pde = create_proc_entry(LVM_LV_SUBDIR, S_IFDIR, - vg_ptr->vg_dir_pde); - if ( pde != NULL) { - vg_ptr->lv_subdir_pde = pde; - for ( l = 0; l < vg_ptr->lv_max; l++) { - if ( ( lv_ptr = vg_ptr->lv[l]) == NULL) continue; - lvm_do_create_proc_entry_of_lv ( vg_ptr, lv_ptr); - } - } - pde = create_proc_entry(LVM_PV_SUBDIR, S_IFDIR, - vg_ptr->vg_dir_pde); - if ( pde != NULL) { - vg_ptr->pv_subdir_pde = pde; - for ( p = 0; p < vg_ptr->pv_max; p++) { - if ( ( pv_ptr = vg_ptr->pv[p]) == NULL) continue; - lvm_do_create_proc_entry_of_pv ( vg_ptr, pv_ptr); - } - } - } -} + if (copy_from_user(&pv_flush_req, arg, + sizeof(pv_flush_req)) != 0) + return -EFAULT; -/* - * remove a /proc entry for a volume group - */ -void lvm_do_remove_proc_entry_of_vg ( vg_t *vg_ptr) { - int l, p; - lv_t *lv_ptr; - pv_t *pv_ptr; + fsync_dev(pv_flush_req.pv_dev); + invalidate_buffers(pv_flush_req.pv_dev); - for ( l = 0; l < vg_ptr->lv_max; l++) { - if ( ( lv_ptr = vg_ptr->lv[l]) == NULL) continue; - lvm_do_remove_proc_entry_of_lv ( vg_ptr, vg_ptr->lv[l]); - } - for ( p = 0; p < vg_ptr->pv_max; p++) { - if ( ( pv_ptr = vg_ptr->pv[p]) == NULL) continue; - lvm_do_remove_proc_entry_of_pv ( vg_ptr, vg_ptr->pv[p]); - } - if ( vg_ptr->vg_dir_pde != NULL) { - remove_proc_entry(LVM_LV_SUBDIR, vg_ptr->vg_dir_pde); - remove_proc_entry(LVM_PV_SUBDIR, vg_ptr->vg_dir_pde); - remove_proc_entry("group", vg_ptr->vg_dir_pde); - remove_proc_entry(vg_ptr->vg_name, lvm_proc_vg_subdir); - } + return 0; } /* * support function initialize gendisk variables */ -void __init lvm_geninit(struct gendisk *lvm_gdisk) +static void __init lvm_geninit(struct gendisk *lvm_gdisk) { int i = 0; @@ -3039,36 +2575,11 @@ blk_size[MAJOR_NR] = lvm_size; blksize_size[MAJOR_NR] = lvm_blocksizes; - hardsect_size[MAJOR_NR] = lvm_blocksizes; + hardsect_size[MAJOR_NR] = lvm_hardsectsizes; return; } /* lvm_gen_init() */ - -/* - * return a pointer to a '-' padded uuid - */ -static char *lvm_show_uuid ( char *uuidstr) { - int i, j; - static char uuid[NAME_LEN] = { 0, }; - - memset ( uuid, 0, NAME_LEN); - - i = 6; - memcpy ( uuid, uuidstr, i); - uuidstr += i; - - for ( j = 0; j < 6; j++) { - uuid[i++] = '-'; - memcpy ( &uuid[i], uuidstr, 4); - uuidstr += 4; - i += 4; - } - - memcpy ( &uuid[i], uuidstr, 2 ); - - return uuid; -} module_init(lvm_init); module_exit(lvm_cleanup); --- 2.4.3aa/drivers/md/lvm-internal.h.orig Fri Apr 6 02:37:24 2001 +++ 2.4.3aa/drivers/md/lvm-internal.h Fri Apr 6 02:37:24 2001 @@ -0,0 +1,104 @@ +/* + * kernel/lvm_internal.h + * + * Copyright (C) 2001 Sistina Software + * + * + * LVM driver is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * LVM driver is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU CC; see the file COPYING. If not, write to + * the Free Software Foundation, 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +/* + * Changelog + * + * 05/01/2001 - Factored this file out of lvm.c (Joe Thornber) + * 11/01/2001 - Renamed lvm_internal and added declarations + * for lvm_fs.c stuff + * + */ + +#ifndef LVM_INTERNAL_H +#define LVM_INTERNAL_H + +#include + +#define _LVM_INTERNAL_H_VERSION "LVM "LVM_RELEASE_NAME" ("LVM_RELEASE_DATE")" + +/* global variables, defined in lvm.c */ +extern char *lvm_version; +extern char *lvm_short_version; +extern ushort lvm_iop_version; +extern int loadtime; +extern const char *const lvm_name; + + +extern vg_t *vg[]; +extern struct file_operations lvm_chr_fops; + +extern struct block_device_operations lvm_blk_dops; + + +/* debug macros */ +#ifdef DEBUG_IOCTL +#define P_IOCTL(fmt, args...) printk(KERN_DEBUG "lvm ioctl: " fmt, ## args) +#else +#define P_IOCTL(fmt, args...) +#endif + +#ifdef DEBUG_MAP +#define P_MAP(fmt, args...) printk(KERN_DEBUG "lvm map: " fmt, ## args) +#else +#define P_MAP(fmt, args...) +#endif + +#ifdef DEBUG_KFREE +#define P_KFREE(fmt, args...) printk(KERN_DEBUG "lvm kfree: " fmt, ## args) +#else +#define P_KFREE(fmt, args...) +#endif + +#ifdef DEBUG_DEVICE +#define P_DEV(fmt, args...) printk(KERN_DEBUG "lvm device: " fmt, ## args) +#else +#define P_DEV(fmt, args...) +#endif + + +/* lvm-snap.c */ +int lvm_get_blksize(kdev_t); +int lvm_snapshot_alloc(lv_t *); +int lvm_snapshot_fill_COW_page(vg_t *, lv_t *); +int lvm_snapshot_COW(kdev_t, ulong, ulong, ulong, vg_t *vg, lv_t *); +int lvm_snapshot_remap_block(kdev_t *, ulong *, ulong, lv_t *); +void lvm_snapshot_release(lv_t *); +int lvm_write_COW_table_block(vg_t *, lv_t *); +void lvm_hash_link(lv_block_exception_t *, kdev_t, ulong, lv_t *); +int lvm_snapshot_alloc_hash_table(lv_t *); +void lvm_drop_snapshot(vg_t *vg, lv_t *, const char *); + + +/* lvm_fs.c */ +void lvm_init_fs(void); +void lvm_fin_fs(void); + +void lvm_fs_create_vg(vg_t *vg_ptr); +void lvm_fs_remove_vg(vg_t *vg_ptr); +void lvm_fs_create_lv(vg_t *vg_ptr, lv_t *lv); +void lvm_fs_remove_lv(vg_t *vg_ptr, lv_t *lv); +void lvm_fs_create_pv(vg_t *vg_ptr, pv_t *pv); +void lvm_fs_remove_pv(vg_t *vg_ptr, pv_t *pv); + +#endif --- 2.4.3aa/drivers/md/lvm-snap.c.orig Sat Feb 10 02:34:09 2001 +++ 2.4.3aa/drivers/md/lvm-snap.c Fri Apr 6 04:37:24 2001 @@ -2,22 +2,22 @@ * kernel/lvm-snap.c * * Copyright (C) 2000 Andrea Arcangeli SuSE - * Heinz Mauelshagen, Sistina Software (persistent snapshots) + * 2000 - 2001 Heinz Mauelshagen, Sistina Software * * LVM snapshot driver is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. - * + * * LVM snapshot driver is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with GNU CC; see the file COPYING. If not, write to * the Free Software Foundation, 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * Boston, MA 02111-1307, USA. * */ @@ -26,6 +26,19 @@ * * 05/07/2000 - implemented persistent snapshot support * 23/11/2000 - used cpu_to_le64 rather than my own macro + * 25/01/2001 - Put LockPage back in + * 01/02/2001 - A dropped snapshot is now set as inactive + * 14/02/2001 - tidied debug statements + * 19/02/2001 - changed rawio calls to pass in preallocated buffer_heads + * 26/02/2001 - introduced __brw_kiovec to remove a lot of conditional + * compiles. + * 07/03/2001 - fixed COW exception table not persistent on 2.2 (HM) + * 12/03/2001 - lvm_pv_get_number changes: + * o made it static + * o renamed it to _pv_get_number + * o pv number is returned in new uint * arg + * o -1 returned on error + * lvm_snapshot_fill_COW_table has a return value too. * */ @@ -38,26 +51,48 @@ #include -#include "lvm-snap.h" +#include "lvm-internal.h" + +static char *lvm_snap_version __attribute__ ((unused)) = "LVM "LVM_RELEASE_NAME" snapshot code ("LVM_RELEASE_DATE")\n"; -static char *lvm_snap_version __attribute__ ((unused)) = "LVM 0.9.1_beta2 snapshot code (18/01/2001)\n"; extern const char *const lvm_name; extern int lvm_blocksizes[]; void lvm_snapshot_release(lv_t *); -uint lvm_pv_get_number(vg_t * vg, kdev_t rdev) -{ +static int _write_COW_table_block(vg_t *vg, lv_t *lv, int idx, + const char **reason); +static void _disable_snapshot(vg_t *vg, lv_t *lv); + + +static inline int __brw_kiovec(int rw, int nr, struct kiobuf *iovec[], + kdev_t dev, unsigned long b[], int size, + lv_t *lv) { + return brw_kiovec(rw, nr, iovec, dev, b, size); +} + + +static int _pv_get_number(vg_t * vg, kdev_t rdev, uint *pvn) { uint p; + for(p = 0; p < vg->pv_max; p++) { + if(vg->pv[p] == NULL) + continue; - for ( p = 0; p < vg->pv_max; p++) - { - if ( vg->pv[p] == NULL) continue; - if ( vg->pv[p]->pv_dev == rdev) break; + if(vg->pv[p]->pv_dev == rdev) + break; + } + + if(p >= vg->pv_max) { + /* bad news, the snapshot COW table is probably corrupt */ + printk(KERN_ERR + "%s -- _pv_get_number failed for rdev = %u\n", + lvm_name, rdev); + return -1; } - return vg->pv[p]->pv_number; + *pvn = vg->pv[p]->pv_number; + return 0; } @@ -133,7 +168,7 @@ return ret; } -void lvm_drop_snapshot(lv_t * lv_snap, const char * reason) +void lvm_drop_snapshot(vg_t *vg, lv_t *lv_snap, const char *reason) { kdev_t last_dev; int i; @@ -142,6 +177,9 @@ or error on this snapshot --> release it */ invalidate_buffers(lv_snap->lv_dev); + /* wipe the snapshot since it's inconsistent now */ + _disable_snapshot(vg, lv_snap); + for (i = last_dev = 0; i < lv_snap->lv_remap_ptr; i++) { if ( lv_snap->lv_block_exception[i].rdev_new != last_dev) { last_dev = lv_snap->lv_block_exception[i].rdev_new; @@ -150,9 +188,10 @@ } lvm_snapshot_release(lv_snap); + lv_snap->lv_status &= ~LV_ACTIVE; printk(KERN_INFO - "%s -- giving up to snapshot %s on %s due %s\n", + "%s -- giving up to snapshot %s on %s: %s\n", lvm_name, lv_snap->lv_snapshot_org->lv_name, lv_snap->lv_name, reason); } @@ -209,129 +248,58 @@ #endif -void lvm_snapshot_fill_COW_page(vg_t * vg, lv_t * lv_snap) +int lvm_snapshot_fill_COW_page(vg_t * vg, lv_t * lv_snap) { - int id = 0, is = lv_snap->lv_remap_ptr; - ulong blksize_snap; - lv_COW_table_disk_t * lv_COW_table = - ( lv_COW_table_disk_t *) page_address(lv_snap->lv_COW_table_page); + uint pvn; + int id = 0, is = lv_snap->lv_remap_ptr; + ulong blksize_snap; + lv_COW_table_disk_t * lv_COW_table = (lv_COW_table_disk_t *) + page_address(lv_snap->lv_COW_table_iobuf->maplist[0]); + + if (is == 0) + return 0; - if (is == 0) return; is--; - blksize_snap = lvm_get_blksize(lv_snap->lv_block_exception[is].rdev_new); + blksize_snap = + lvm_get_blksize(lv_snap->lv_block_exception[is].rdev_new); is -= is % (blksize_snap / sizeof(lv_COW_table_disk_t)); memset(lv_COW_table, 0, blksize_snap); for ( ; is < lv_snap->lv_remap_ptr; is++, id++) { /* store new COW_table entry */ - lv_COW_table[id].pv_org_number = cpu_to_le64(lvm_pv_get_number(vg, lv_snap->lv_block_exception[is].rdev_org)); - lv_COW_table[id].pv_org_rsector = cpu_to_le64(lv_snap->lv_block_exception[is].rsector_org); - lv_COW_table[id].pv_snap_number = cpu_to_le64(lvm_pv_get_number(vg, lv_snap->lv_block_exception[is].rdev_new)); - lv_COW_table[id].pv_snap_rsector = cpu_to_le64(lv_snap->lv_block_exception[is].rsector_new); + lv_block_exception_t *be = lv_snap->lv_block_exception + is; + if(_pv_get_number(vg, be->rdev_org, &pvn)) + goto bad; + + lv_COW_table[id].pv_org_number = cpu_to_le64(pvn); + lv_COW_table[id].pv_org_rsector = cpu_to_le64(be->rsector_org); + if(_pv_get_number(vg, be->rdev_new, &pvn)) + goto bad; + + lv_COW_table[id].pv_snap_number = cpu_to_le64(pvn); + lv_COW_table[id].pv_snap_rsector = + cpu_to_le64(be->rsector_new); } + + return 0; + + bad: + printk(KERN_ERR "%s -- lvm_snapshot_fill_COW_page failed", lvm_name); + return -1; } /* * writes a COW exception table sector to disk (HM) - * */ - -int lvm_write_COW_table_block(vg_t * vg, lv_t * lv_snap) +int lvm_write_COW_table_block(vg_t * vg, lv_t *lv_snap) { - int blksize_snap; - int end_of_table; - int idx = lv_snap->lv_remap_ptr, idx_COW_table; - int nr_pages_tmp; - int length_tmp; - ulong snap_pe_start, COW_table_sector_offset, - COW_entries_per_pe, COW_chunks_per_pe, COW_entries_per_block; - ulong blocks[1]; - const char * reason; - kdev_t snap_phys_dev; - struct kiobuf * iobuf = lv_snap->lv_iobuf; - struct page * page_tmp; - lv_COW_table_disk_t * lv_COW_table = - ( lv_COW_table_disk_t *) page_address(lv_snap->lv_COW_table_page); - - idx--; - - COW_chunks_per_pe = LVM_GET_COW_TABLE_CHUNKS_PER_PE(vg, lv_snap); - COW_entries_per_pe = LVM_GET_COW_TABLE_ENTRIES_PER_PE(vg, lv_snap); - - /* get physical addresse of destination chunk */ - snap_phys_dev = lv_snap->lv_block_exception[idx].rdev_new; - snap_pe_start = lv_snap->lv_block_exception[idx - (idx % COW_entries_per_pe)].rsector_new - lv_snap->lv_chunk_size; - - blksize_snap = lvm_get_blksize(snap_phys_dev); - - COW_entries_per_block = blksize_snap / sizeof(lv_COW_table_disk_t); - idx_COW_table = idx % COW_entries_per_pe % COW_entries_per_block; - - if ( idx_COW_table == 0) memset(lv_COW_table, 0, blksize_snap); - - /* sector offset into the on disk COW table */ - COW_table_sector_offset = (idx % COW_entries_per_pe) / (SECTOR_SIZE / sizeof(lv_COW_table_disk_t)); - - /* COW table block to write next */ - blocks[0] = (snap_pe_start + COW_table_sector_offset) >> (blksize_snap >> 10); - - /* store new COW_table entry */ - lv_COW_table[idx_COW_table].pv_org_number = cpu_to_le64(lvm_pv_get_number(vg, lv_snap->lv_block_exception[idx].rdev_org)); - lv_COW_table[idx_COW_table].pv_org_rsector = cpu_to_le64(lv_snap->lv_block_exception[idx].rsector_org); - lv_COW_table[idx_COW_table].pv_snap_number = cpu_to_le64(lvm_pv_get_number(vg, snap_phys_dev)); - lv_COW_table[idx_COW_table].pv_snap_rsector = cpu_to_le64(lv_snap->lv_block_exception[idx].rsector_new); - - length_tmp = iobuf->length; - iobuf->length = blksize_snap; - page_tmp = iobuf->maplist[0]; - iobuf->maplist[0] = lv_snap->lv_COW_table_page; - nr_pages_tmp = iobuf->nr_pages; - iobuf->nr_pages = 1; - - if (brw_kiovec(WRITE, 1, &iobuf, snap_phys_dev, - blocks, blksize_snap) != blksize_snap) - goto fail_raw_write; - - - /* initialization of next COW exception table block with zeroes */ - end_of_table = idx % COW_entries_per_pe == COW_entries_per_pe - 1; - if (idx_COW_table % COW_entries_per_block == COW_entries_per_block - 1 || end_of_table) - { - /* don't go beyond the end */ - if (idx + 1 >= lv_snap->lv_remap_end) goto good_out; - - memset(lv_COW_table, 0, blksize_snap); - - if (end_of_table) - { - idx++; - snap_phys_dev = lv_snap->lv_block_exception[idx].rdev_new; - snap_pe_start = lv_snap->lv_block_exception[idx - (idx % COW_entries_per_pe)].rsector_new - lv_snap->lv_chunk_size; - blksize_snap = lvm_get_blksize(snap_phys_dev); - blocks[0] = snap_pe_start >> (blksize_snap >> 10); - } else blocks[0]++; - - if (brw_kiovec(WRITE, 1, &iobuf, snap_phys_dev, - blocks, blksize_snap) != blksize_snap) - goto fail_raw_write; - } - - - good_out: - iobuf->length = length_tmp; - iobuf->maplist[0] = page_tmp; - iobuf->nr_pages = nr_pages_tmp; - return 0; - - /* slow path */ - out: - lvm_drop_snapshot(lv_snap, reason); - return 1; - - fail_raw_write: - reason = "write error"; - goto out; + int r; + const char *err; + if((r = _write_COW_table_block(vg, lv_snap, + lv_snap->lv_remap_ptr - 1, &err))) + lvm_drop_snapshot(vg, lv_snap, err); + return r; } /* @@ -346,7 +314,7 @@ unsigned long org_phys_sector, unsigned long org_pe_start, unsigned long org_virt_sector, - lv_t * lv_snap) + vg_t *vg, lv_t* lv_snap) { const char * reason; unsigned long org_start, snap_start, snap_phys_dev, virt_start, pe_off; @@ -372,13 +340,11 @@ #ifdef DEBUG_SNAPSHOT printk(KERN_INFO "%s -- COW: " - "org %02d:%02d faulting %lu start %lu, " - "snap %02d:%02d start %lu, " + "org %s faulting %lu start %lu, snap %s start %lu, " "size %d, pe_start %lu pe_off %lu, virt_sec %lu\n", lvm_name, - MAJOR(org_phys_dev), MINOR(org_phys_dev), org_phys_sector, - org_start, - MAJOR(snap_phys_dev), MINOR(snap_phys_dev), snap_start, + kdevname(org_phys_dev), org_phys_sector, org_start, + kdevname(snap_phys_dev), snap_start, chunk_size, org_pe_start, pe_off, org_virt_sector); @@ -404,14 +370,14 @@ lvm_snapshot_prepare_blocks(blocks, org_start, nr_sectors, blksize_org); - if (brw_kiovec(READ, 1, &iobuf, org_phys_dev, - blocks, blksize_org) != (nr_sectors<<9)) + if (__brw_kiovec(READ, 1, &iobuf, org_phys_dev, blocks, + blksize_org, lv_snap) != (nr_sectors<<9)) goto fail_raw_read; lvm_snapshot_prepare_blocks(blocks, snap_start, nr_sectors, blksize_snap); - if (brw_kiovec(WRITE, 1, &iobuf, snap_phys_dev, - blocks, blksize_snap) != (nr_sectors<<9)) + if (__brw_kiovec(WRITE, 1, &iobuf, snap_phys_dev, blocks, + blksize_snap, lv_snap) != (nr_sectors<<9)) goto fail_raw_write; } @@ -436,20 +402,20 @@ return 0; /* slow path */ - out: - lvm_drop_snapshot(lv_snap, reason); +out: + lvm_drop_snapshot(vg, lv_snap, reason); return 1; - fail_out_of_space: +fail_out_of_space: reason = "out of space"; goto out; - fail_raw_read: +fail_raw_read: reason = "read error"; goto out; - fail_raw_write: +fail_raw_write: reason = "write error"; goto out; - fail_blksize: +fail_blksize: reason = "blocksize error"; goto out; } @@ -458,30 +424,30 @@ { int bytes, nr_pages, err, i; - bytes = sectors << 9; + bytes = sectors * SECTOR_SIZE; nr_pages = (bytes + ~PAGE_MASK) >> PAGE_SHIFT; err = expand_kiobuf(iobuf, nr_pages); - if (err) - goto out; + if (err) goto out; err = -ENOMEM; - iobuf->locked = 0; + iobuf->locked = 1; iobuf->nr_pages = 0; for (i = 0; i < nr_pages; i++) { struct page * page; page = alloc_page(GFP_KERNEL); - if (!page) - goto out; + if (!page) goto out; iobuf->maplist[i] = page; + LockPage(page); iobuf->nr_pages++; } iobuf->offset = 0; err = 0; - out: + +out: return err; } @@ -523,41 +489,49 @@ while (buckets--) INIT_LIST_HEAD(hash+buckets); err = 0; - out: +out: return err; } int lvm_snapshot_alloc(lv_t * lv_snap) { - int err, blocksize, max_sectors; + int ret, max_sectors; - err = alloc_kiovec(1, &lv_snap->lv_iobuf); - if (err) - goto out; + /* allocate kiovec to do chunk io */ + ret = alloc_kiovec(1, &lv_snap->lv_iobuf); + if (ret) goto out; - blocksize = lvm_blocksizes[MINOR(lv_snap->lv_dev)]; max_sectors = KIO_MAX_SECTORS << (PAGE_SHIFT-9); - err = lvm_snapshot_alloc_iobuf_pages(lv_snap->lv_iobuf, max_sectors); - if (err) - goto out_free_kiovec; + ret = lvm_snapshot_alloc_iobuf_pages(lv_snap->lv_iobuf, max_sectors); + if (ret) goto out_free_kiovec; - err = lvm_snapshot_alloc_hash_table(lv_snap); - if (err) - goto out_free_kiovec; + /* allocate kiovec to do exception table io */ + ret = alloc_kiovec(1, &lv_snap->lv_COW_table_iobuf); + if (ret) goto out_free_kiovec; + ret = lvm_snapshot_alloc_iobuf_pages(lv_snap->lv_COW_table_iobuf, + PAGE_SIZE/SECTOR_SIZE); + if (ret) goto out_free_both_kiovecs; - lv_snap->lv_COW_table_page = alloc_page(GFP_KERNEL); - if (!lv_snap->lv_COW_table_page) - goto out_free_kiovec; + ret = lvm_snapshot_alloc_hash_table(lv_snap); + if (ret) goto out_free_both_kiovecs; - out: - return err; - out_free_kiovec: +out: + return ret; + +out_free_both_kiovecs: + unmap_kiobuf(lv_snap->lv_COW_table_iobuf); + free_kiovec(1, &lv_snap->lv_COW_table_iobuf); + lv_snap->lv_COW_table_iobuf = NULL; + +out_free_kiovec: unmap_kiobuf(lv_snap->lv_iobuf); free_kiovec(1, &lv_snap->lv_iobuf); - vfree(lv_snap->lv_snapshot_hash_table); + lv_snap->lv_iobuf = NULL; + if (lv_snap->lv_snapshot_hash_table != NULL) + vfree(lv_snap->lv_snapshot_hash_table); lv_snap->lv_snapshot_hash_table = NULL; goto out; } @@ -582,9 +556,125 @@ free_kiovec(1, &lv->lv_iobuf); lv->lv_iobuf = NULL; } - if (lv->lv_COW_table_page) + if (lv->lv_COW_table_iobuf) { - free_page((ulong)lv->lv_COW_table_page); - lv->lv_COW_table_page = NULL; + kiobuf_wait_for_io(lv->lv_COW_table_iobuf); + unmap_kiobuf(lv->lv_COW_table_iobuf); + free_kiovec(1, &lv->lv_COW_table_iobuf); + lv->lv_COW_table_iobuf = NULL; + } +} + + +static int _write_COW_table_block(vg_t *vg, lv_t *lv_snap, + int idx, const char **reason) { + int blksize_snap; + int end_of_table; + int idx_COW_table; + uint pvn; + ulong snap_pe_start, COW_table_sector_offset, + COW_entries_per_pe, COW_chunks_per_pe, COW_entries_per_block; + ulong blocks[1]; + kdev_t snap_phys_dev; + lv_block_exception_t *be; + struct kiobuf *COW_table_iobuf = lv_snap->lv_COW_table_iobuf; + lv_COW_table_disk_t * lv_COW_table = + ( lv_COW_table_disk_t *) page_address(lv_snap->lv_COW_table_iobuf->maplist[0]); + + COW_chunks_per_pe = LVM_GET_COW_TABLE_CHUNKS_PER_PE(vg, lv_snap); + COW_entries_per_pe = LVM_GET_COW_TABLE_ENTRIES_PER_PE(vg, lv_snap); + + /* get physical addresse of destination chunk */ + snap_phys_dev = lv_snap->lv_block_exception[idx].rdev_new; + snap_pe_start = lv_snap->lv_block_exception[idx - (idx % COW_entries_per_pe)].rsector_new - lv_snap->lv_chunk_size; + + blksize_snap = lvm_get_blksize(snap_phys_dev); + + COW_entries_per_block = blksize_snap / sizeof(lv_COW_table_disk_t); + idx_COW_table = idx % COW_entries_per_pe % COW_entries_per_block; + + if ( idx_COW_table == 0) memset(lv_COW_table, 0, blksize_snap); + + /* sector offset into the on disk COW table */ + COW_table_sector_offset = (idx % COW_entries_per_pe) / (SECTOR_SIZE / sizeof(lv_COW_table_disk_t)); + + /* COW table block to write next */ + blocks[0] = (snap_pe_start + COW_table_sector_offset) >> (blksize_snap >> 10); + + /* store new COW_table entry */ + be = lv_snap->lv_block_exception + idx; + if(_pv_get_number(vg, be->rdev_org, &pvn)) + goto fail_pv_get_number; + + lv_COW_table[idx_COW_table].pv_org_number = cpu_to_le64(pvn); + lv_COW_table[idx_COW_table].pv_org_rsector = + cpu_to_le64(be->rsector_org); + if(_pv_get_number(vg, snap_phys_dev, &pvn)) + goto fail_pv_get_number; + + lv_COW_table[idx_COW_table].pv_snap_number = cpu_to_le64(pvn); + lv_COW_table[idx_COW_table].pv_snap_rsector = + cpu_to_le64(be->rsector_new); + + COW_table_iobuf->length = blksize_snap; + /* COW_table_iobuf->nr_pages = 1; */ + + if (__brw_kiovec(WRITE, 1, &COW_table_iobuf, snap_phys_dev, + blocks, blksize_snap, lv_snap) != blksize_snap) + goto fail_raw_write; + + /* initialization of next COW exception table block with zeroes */ + end_of_table = idx % COW_entries_per_pe == COW_entries_per_pe - 1; + if (idx_COW_table % COW_entries_per_block == COW_entries_per_block - 1 || end_of_table) + { + /* don't go beyond the end */ + if (idx + 1 >= lv_snap->lv_remap_end) goto out; + + memset(lv_COW_table, 0, blksize_snap); + + if (end_of_table) + { + idx++; + snap_phys_dev = lv_snap->lv_block_exception[idx].rdev_new; + snap_pe_start = lv_snap->lv_block_exception[idx - (idx % COW_entries_per_pe)].rsector_new - lv_snap->lv_chunk_size; + blksize_snap = lvm_get_blksize(snap_phys_dev); + blocks[0] = snap_pe_start >> (blksize_snap >> 10); + } else blocks[0]++; + + if (__brw_kiovec(WRITE, 1, &COW_table_iobuf, snap_phys_dev, + blocks, blksize_snap, lv_snap) != + blksize_snap) + goto fail_raw_write; + } + +out: + return 0; + +fail_raw_write: + *reason = "write error"; + return 1; + +fail_pv_get_number: + *reason = "_pv_get_number failed"; + return 1; +} + +/* + * FIXME_1.2 + * This function is a bit of a hack; we need to ensure that the + * snapshot is never made active again, because it will surely be + * corrupt. At the moment we do not have access to the LVM metadata + * from within the kernel. So we set the first exception to point to + * sector 1 (which will always be within the metadata, and as such + * invalid). User land tools will check for this when they are asked + * to activate the snapshot and prevent this from happening. + */ + +static void _disable_snapshot(vg_t *vg, lv_t *lv) { + const char *err; + lv->lv_block_exception[0].rsector_org = LVM_SNAPSHOT_DROPPED_SECTOR; + if(_write_COW_table_block(vg, lv, 0, &err) < 0) { + printk(KERN_ERR "%s -- couldn't disable snapshot: %s\n", + lvm_name, err); } } --- 2.4.3aa/drivers/md/lvm-fs.c.orig Fri Apr 6 02:37:24 2001 +++ 2.4.3aa/drivers/md/lvm-fs.c Fri Apr 6 02:37:24 2001 @@ -0,0 +1,596 @@ +/* + * kernel/lvm-fs.c + * + * Copyright (C) 2001 Sistina Software + * + * January,February 2001 + * + * LVM driver is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * LVM driver is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU CC; see the file COPYING. If not, write to + * the Free Software Foundation, 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +/* + * Changelog + * + * 11/01/2001 - First version (Joe Thornber) + * + */ + +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include "lvm-internal.h" + + +static int _proc_read_vg(char *page, char **start, off_t off, + int count, int *eof, void *data); +static int _proc_read_lv(char *page, char **start, off_t off, + int count, int *eof, void *data); +static int _proc_read_pv(char *page, char **start, off_t off, + int count, int *eof, void *data); +static int _proc_read_global(char *page, char **start, off_t off, + int count, int *eof, void *data); + +static int _vg_info(vg_t *vg_ptr, char *buf); +static int _lv_info(vg_t *vg_ptr, lv_t *lv_ptr, char *buf); +static int _pv_info(pv_t *pv_ptr, char *buf); + +static void _show_uuid(const char *src, char *b, char *e); + +static devfs_handle_t lvm_devfs_handle; +static devfs_handle_t vg_devfs_handle[MAX_VG]; +static devfs_handle_t ch_devfs_handle[MAX_VG]; +static devfs_handle_t lv_devfs_handle[MAX_LV]; + +static struct proc_dir_entry *lvm_proc_dir = NULL; +static struct proc_dir_entry *lvm_proc_vg_subdir = NULL; + +/* inline functions */ + +/* public interface */ +void lvm_init_fs() { + struct proc_dir_entry *pde; + + lvm_devfs_handle = devfs_register( + 0 , "lvm", 0, 0, LVM_CHAR_MAJOR, + S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP, + &lvm_chr_fops, NULL); + + lvm_proc_dir = create_proc_entry(LVM_DIR, S_IFDIR, &proc_root); + if (lvm_proc_dir) { + lvm_proc_vg_subdir = create_proc_entry(LVM_VG_SUBDIR, S_IFDIR, + lvm_proc_dir); + pde = create_proc_entry(LVM_GLOBAL, S_IFREG, lvm_proc_dir); + if ( pde != NULL) pde->read_proc = _proc_read_global; + } +} + +void lvm_fin_fs() { + devfs_unregister (lvm_devfs_handle); + + remove_proc_entry(LVM_GLOBAL, lvm_proc_dir); + remove_proc_entry(LVM_VG_SUBDIR, lvm_proc_dir); + remove_proc_entry(LVM_DIR, &proc_root); +} + +void lvm_fs_create_vg(vg_t *vg_ptr) { + struct proc_dir_entry *pde; + + vg_devfs_handle[vg_ptr->vg_number] = + devfs_mk_dir(0, vg_ptr->vg_name, NULL); + + ch_devfs_handle[vg_ptr->vg_number] = devfs_register( + vg_devfs_handle[vg_ptr->vg_number] , "group", + DEVFS_FL_DEFAULT, LVM_CHAR_MAJOR, vg_ptr->vg_number, + S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP, + &lvm_chr_fops, NULL); + + vg_ptr->vg_dir_pde = create_proc_entry(vg_ptr->vg_name, S_IFDIR, + lvm_proc_vg_subdir); + + if((pde = create_proc_entry("group", S_IFREG, vg_ptr->vg_dir_pde))) { + pde->read_proc = _proc_read_vg; + pde->data = vg_ptr; + } + + vg_ptr->lv_subdir_pde = + create_proc_entry(LVM_LV_SUBDIR, S_IFDIR, vg_ptr->vg_dir_pde); + + vg_ptr->pv_subdir_pde = + create_proc_entry(LVM_PV_SUBDIR, S_IFDIR, vg_ptr->vg_dir_pde); +} + +void lvm_fs_remove_vg(vg_t *vg_ptr) { + int i; + + devfs_unregister(ch_devfs_handle[vg_ptr->vg_number]); + devfs_unregister(vg_devfs_handle[vg_ptr->vg_number]); + + /* remove lv's */ + for(i = 0; i < vg_ptr->lv_max; i++) + if(vg_ptr->lv[i]) lvm_fs_remove_lv(vg_ptr, vg_ptr->lv[i]); + + /* remove pv's */ + for(i = 0; i < vg_ptr->pv_max; i++) + if(vg_ptr->pv[i]) lvm_fs_remove_pv(vg_ptr, vg_ptr->pv[i]); + + if(vg_ptr->vg_dir_pde) { + remove_proc_entry(LVM_LV_SUBDIR, vg_ptr->vg_dir_pde); + remove_proc_entry(LVM_PV_SUBDIR, vg_ptr->vg_dir_pde); + remove_proc_entry("group", vg_ptr->vg_dir_pde); + remove_proc_entry(vg_ptr->vg_name, lvm_proc_vg_subdir); + } +} + + +static inline const char *_basename(const char *str) { + const char *name = strrchr(str, '/'); + name = name ? name + 1 : str; + return name; +} + +void lvm_fs_create_lv(vg_t *vg_ptr, lv_t *lv) { + struct proc_dir_entry *pde; + const char *name = _basename(lv->lv_name); + + lv_devfs_handle[MINOR(lv->lv_dev)] = devfs_register( + vg_devfs_handle[vg_ptr->vg_number], name, + DEVFS_FL_DEFAULT, LVM_BLK_MAJOR, MINOR(lv->lv_dev), + S_IFBLK | S_IRUSR | S_IWUSR | S_IRGRP, + &lvm_blk_dops, NULL); + + if(vg_ptr->lv_subdir_pde && + (pde = create_proc_entry(name, S_IFREG, vg_ptr->lv_subdir_pde))) { + pde->read_proc = _proc_read_lv; + pde->data = lv; + } +} + +void lvm_fs_remove_lv(vg_t *vg_ptr, lv_t *lv) { + devfs_unregister(lv_devfs_handle[MINOR(lv->lv_dev)]); + + if(vg_ptr->lv_subdir_pde) { + const char *name = _basename(lv->lv_name); + remove_proc_entry(name, vg_ptr->lv_subdir_pde); + } +} + + +static inline void _make_pv_name(const char *src, char *b, char *e) { + int offset = strlen(LVM_DIR_PREFIX); + if(strncmp(src, LVM_DIR_PREFIX, offset)) + offset = 0; + + e--; + src += offset; + while(*src && (b != e)) { + *b++ = (*src == '/') ? '_' : *src; + src++; + } + *b = '\0'; +} + +void lvm_fs_create_pv(vg_t *vg_ptr, pv_t *pv) { + struct proc_dir_entry *pde; + char name[NAME_LEN]; + + if(!vg_ptr->pv_subdir_pde) + return; + + _make_pv_name(pv->pv_name, name, name + sizeof(name)); + if((pde = create_proc_entry(name, S_IFREG, vg_ptr->pv_subdir_pde))) { + pde->read_proc = _proc_read_pv; + pde->data = pv; + } +} + +void lvm_fs_remove_pv(vg_t *vg_ptr, pv_t *pv) { + char name[NAME_LEN]; + + if(!vg_ptr->pv_subdir_pde) + return; + + _make_pv_name(pv->pv_name, name, name + sizeof(name)); + remove_proc_entry(name, vg_ptr->pv_subdir_pde); +} + + +static int _proc_read_vg(char *page, char **start, off_t off, + int count, int *eof, void *data) { + int sz = 0; + vg_t *vg_ptr = data; + char uuid[NAME_LEN]; + + sz += sprintf(page + sz, "name: %s\n", vg_ptr->vg_name); + sz += sprintf(page + sz, "size: %u\n", + vg_ptr->pe_total * vg_ptr->pe_size / 2); + sz += sprintf(page + sz, "access: %u\n", vg_ptr->vg_access); + sz += sprintf(page + sz, "status: %u\n", vg_ptr->vg_status); + sz += sprintf(page + sz, "number: %u\n", vg_ptr->vg_number); + sz += sprintf(page + sz, "LV max: %u\n", vg_ptr->lv_max); + sz += sprintf(page + sz, "LV current: %u\n", vg_ptr->lv_cur); + sz += sprintf(page + sz, "LV open: %u\n", vg_ptr->lv_open); + sz += sprintf(page + sz, "PV max: %u\n", vg_ptr->pv_max); + sz += sprintf(page + sz, "PV current: %u\n", vg_ptr->pv_cur); + sz += sprintf(page + sz, "PV active: %u\n", vg_ptr->pv_act); + sz += sprintf(page + sz, "PE size: %u\n", vg_ptr->pe_size / 2); + sz += sprintf(page + sz, "PE total: %u\n", vg_ptr->pe_total); + sz += sprintf(page + sz, "PE allocated: %u\n", vg_ptr->pe_allocated); + + _show_uuid(vg_ptr->vg_uuid, uuid, uuid + sizeof(uuid)); + sz += sprintf(page + sz, "uuid: %s\n", uuid); + + return sz; +} + +static int _proc_read_lv(char *page, char **start, off_t off, + int count, int *eof, void *data) { + int sz = 0; + lv_t *lv = data; + + sz += sprintf(page + sz, "name: %s\n", lv->lv_name); + sz += sprintf(page + sz, "size: %u\n", lv->lv_size); + sz += sprintf(page + sz, "access: %u\n", lv->lv_access); + sz += sprintf(page + sz, "status: %u\n", lv->lv_status); + sz += sprintf(page + sz, "number: %u\n", lv->lv_number); + sz += sprintf(page + sz, "open: %u\n", lv->lv_open); + sz += sprintf(page + sz, "allocation: %u\n", lv->lv_allocation); + sz += sprintf(page + sz, "device: %02u:%02u\n", + MAJOR(lv->lv_dev), MINOR(lv->lv_dev)); + + return sz; +} + +static int _proc_read_pv(char *page, char **start, off_t off, + int count, int *eof, void *data) { + int sz = 0; + pv_t *pv = data; + char uuid[NAME_LEN]; + + sz += sprintf(page + sz, "name: %s\n", pv->pv_name); + sz += sprintf(page + sz, "size: %u\n", pv->pv_size); + sz += sprintf(page + sz, "status: %u\n", pv->pv_status); + sz += sprintf(page + sz, "number: %u\n", pv->pv_number); + sz += sprintf(page + sz, "allocatable: %u\n", pv->pv_allocatable); + sz += sprintf(page + sz, "LV current: %u\n", pv->lv_cur); + sz += sprintf(page + sz, "PE size: %u\n", pv->pe_size / 2); + sz += sprintf(page + sz, "PE total: %u\n", pv->pe_total); + sz += sprintf(page + sz, "PE allocated: %u\n", pv->pe_allocated); + sz += sprintf(page + sz, "device: %02u:%02u\n", + MAJOR(pv->pv_dev), MINOR(pv->pv_dev)); + + _show_uuid(pv->pv_uuid, uuid, uuid + sizeof(uuid)); + sz += sprintf(page + sz, "uuid: %s\n", uuid); + + return sz; +} + +static int _proc_read_global(char *page, char **start, off_t pos, int count, + int *eof, void *data) { + +#define LVM_PROC_BUF ( i == 0 ? dummy_buf : &buf[sz]) + + int c, i, l, p, v, vg_counter, pv_counter, lv_counter, lv_open_counter, + lv_open_total, pe_t_bytes, hash_table_bytes, lv_block_exception_t_bytes, seconds; + static off_t sz; + off_t sz_last; + static char *buf = NULL; + static char dummy_buf[160]; /* sized for 2 lines */ + vg_t *vg_ptr; + lv_t *lv_ptr; + pv_t *pv_ptr; + + +#ifdef DEBUG_LVM_PROC_GET_INFO + printk(KERN_DEBUG + "%s - lvm_proc_get_global_info CALLED pos: %lu count: %d whence: %d\n", + lvm_name, pos, count, whence); +#endif + + if(pos != 0 && buf != NULL) + goto out; + + sz_last = vg_counter = pv_counter = lv_counter = lv_open_counter = \ + lv_open_total = pe_t_bytes = hash_table_bytes = \ + lv_block_exception_t_bytes = 0; + + /* get some statistics */ + for (v = 0; v < ABS_MAX_VG; v++) { + if ((vg_ptr = vg[v]) != NULL) { + vg_counter++; + pv_counter += vg_ptr->pv_cur; + lv_counter += vg_ptr->lv_cur; + if (vg_ptr->lv_cur > 0) { + for (l = 0; l < vg[v]->lv_max; l++) { + if ((lv_ptr = vg_ptr->lv[l]) != NULL) { + pe_t_bytes += lv_ptr->lv_allocated_le; + hash_table_bytes += lv_ptr->lv_snapshot_hash_table_size; + if (lv_ptr->lv_block_exception != NULL) + lv_block_exception_t_bytes += lv_ptr->lv_remap_end; + if (lv_ptr->lv_open > 0) { + lv_open_counter++; + lv_open_total += lv_ptr->lv_open; + } + } + } + } + } + } + + pe_t_bytes *= sizeof(pe_t); + lv_block_exception_t_bytes *= sizeof(lv_block_exception_t); + + if (buf != NULL) { + P_KFREE("%s -- vfree %d\n", lvm_name, __LINE__); + lock_kernel(); + vfree(buf); + unlock_kernel(); + buf = NULL; + } + /* 2 times: first to get size to allocate buffer, + 2nd to fill the malloced buffer */ + for (i = 0; i < 2; i++) { + sz = 0; + sz += sprintf(LVM_PROC_BUF, + "LVM " +#ifdef MODULE + "module" +#else + "driver" +#endif + " %s\n\n" + "Total: %d VG%s %d PV%s %d LV%s ", + lvm_short_version, + vg_counter, vg_counter == 1 ? "" : "s", + pv_counter, pv_counter == 1 ? "" : "s", + lv_counter, lv_counter == 1 ? "" : "s"); + sz += sprintf(LVM_PROC_BUF, + "(%d LV%s open", + lv_open_counter, + lv_open_counter == 1 ? "" : "s"); + if (lv_open_total > 0) + sz += sprintf(LVM_PROC_BUF, + " %d times)\n", + lv_open_total); + else + sz += sprintf(LVM_PROC_BUF, ")"); + sz += sprintf(LVM_PROC_BUF, + "\nGlobal: %lu bytes malloced IOP version: %d ", + vg_counter * sizeof(vg_t) + + pv_counter * sizeof(pv_t) + + lv_counter * sizeof(lv_t) + + pe_t_bytes + hash_table_bytes + lv_block_exception_t_bytes + sz_last, + lvm_iop_version); + + seconds = CURRENT_TIME - loadtime; + if (seconds < 0) + loadtime = CURRENT_TIME + seconds; + if (seconds / 86400 > 0) { + sz += sprintf(LVM_PROC_BUF, "%d day%s ", + seconds / 86400, + seconds / 86400 == 0 || + seconds / 86400 > 1 ? "s" : ""); + } + sz += sprintf(LVM_PROC_BUF, "%d:%02d:%02d active\n", + (seconds % 86400) / 3600, + (seconds % 3600) / 60, + seconds % 60); + + if (vg_counter > 0) { + for (v = 0; v < ABS_MAX_VG; v++) { + /* volume group */ + if ((vg_ptr = vg[v]) != NULL) { + sz += _vg_info(vg_ptr, LVM_PROC_BUF); + + /* physical volumes */ + sz += sprintf(LVM_PROC_BUF, + "\n PV%s ", + vg_ptr->pv_cur == 1 ? ": " : "s:"); + c = 0; + for (p = 0; p < vg_ptr->pv_max; p++) { + if ((pv_ptr = vg_ptr->pv[p]) != NULL) { + sz += _pv_info(pv_ptr, LVM_PROC_BUF); + + c++; + if (c < vg_ptr->pv_cur) + sz += sprintf(LVM_PROC_BUF, + "\n "); + } + } + + /* logical volumes */ + sz += sprintf(LVM_PROC_BUF, + "\n LV%s ", + vg_ptr->lv_cur == 1 ? ": " : "s:"); + c = 0; + for (l = 0; l < vg_ptr->lv_max; l++) { + if ((lv_ptr = vg_ptr->lv[l]) != NULL) { + sz += _lv_info(vg_ptr, lv_ptr, LVM_PROC_BUF); + c++; + if (c < vg_ptr->lv_cur) + sz += sprintf(LVM_PROC_BUF, + "\n "); + } + } + if (vg_ptr->lv_cur == 0) sz += sprintf(LVM_PROC_BUF, "none"); + sz += sprintf(LVM_PROC_BUF, "\n"); + } + } + } + if (buf == NULL) { + lock_kernel(); + buf = vmalloc(sz); + unlock_kernel(); + if (buf == NULL) { + sz = 0; + return sprintf(page, "%s - vmalloc error at line %d\n", + lvm_name, __LINE__); + } + } + sz_last = sz; + } + + out: + if (pos > sz - 1) { + lock_kernel(); + vfree(buf); + unlock_kernel(); + buf = NULL; + return 0; + } + *start = &buf[pos]; + if (sz - pos < count) + return sz - pos; + else + return count; + +#undef LVM_PROC_BUF +} + +/* + * provide VG info for proc filesystem use (global) + */ +static int _vg_info(vg_t *vg_ptr, char *buf) { + int sz = 0; + char inactive_flag = ' '; + + if (!(vg_ptr->vg_status & VG_ACTIVE)) inactive_flag = 'I'; + sz = sprintf(buf, + "\nVG: %c%s [%d PV, %d LV/%d open] " + " PE Size: %d KB\n" + " Usage [KB/PE]: %d /%d total " + "%d /%d used %d /%d free", + inactive_flag, + vg_ptr->vg_name, + vg_ptr->pv_cur, + vg_ptr->lv_cur, + vg_ptr->lv_open, + vg_ptr->pe_size >> 1, + vg_ptr->pe_size * vg_ptr->pe_total >> 1, + vg_ptr->pe_total, + vg_ptr->pe_allocated * vg_ptr->pe_size >> 1, + vg_ptr->pe_allocated, + (vg_ptr->pe_total - vg_ptr->pe_allocated) * + vg_ptr->pe_size >> 1, + vg_ptr->pe_total - vg_ptr->pe_allocated); + return sz; +} + + +/* + * provide LV info for proc filesystem use (global) + */ +static int _lv_info(vg_t *vg_ptr, lv_t *lv_ptr, char *buf) { + int sz = 0; + char inactive_flag = 'A', allocation_flag = ' ', + stripes_flag = ' ', rw_flag = ' ', *basename; + + if (!(lv_ptr->lv_status & LV_ACTIVE)) + inactive_flag = 'I'; + rw_flag = 'R'; + if (lv_ptr->lv_access & LV_WRITE) + rw_flag = 'W'; + allocation_flag = 'D'; + if (lv_ptr->lv_allocation & LV_CONTIGUOUS) + allocation_flag = 'C'; + stripes_flag = 'L'; + if (lv_ptr->lv_stripes > 1) + stripes_flag = 'S'; + sz += sprintf(buf+sz, + "[%c%c%c%c", + inactive_flag, + rw_flag, + allocation_flag, + stripes_flag); + if (lv_ptr->lv_stripes > 1) + sz += sprintf(buf+sz, "%-2d", + lv_ptr->lv_stripes); + else + sz += sprintf(buf+sz, " "); + + /* FIXME: use _basename */ + basename = strrchr(lv_ptr->lv_name, '/'); + if ( basename == 0) basename = lv_ptr->lv_name; + else basename++; + sz += sprintf(buf+sz, "] %-25s", basename); + if (strlen(basename) > 25) + sz += sprintf(buf+sz, + "\n "); + sz += sprintf(buf+sz, "%9d /%-6d ", + lv_ptr->lv_size >> 1, + lv_ptr->lv_size / vg_ptr->pe_size); + + if (lv_ptr->lv_open == 0) + sz += sprintf(buf+sz, "close"); + else + sz += sprintf(buf+sz, "%dx open", + lv_ptr->lv_open); + + return sz; +} + + +/* + * provide PV info for proc filesystem use (global) + */ +static int _pv_info(pv_t *pv, char *buf) { + int sz = 0; + char inactive_flag = 'A', allocation_flag = ' '; + char *pv_name = NULL; + + if (!(pv->pv_status & PV_ACTIVE)) + inactive_flag = 'I'; + allocation_flag = 'A'; + if (!(pv->pv_allocatable & PV_ALLOCATABLE)) + allocation_flag = 'N'; + pv_name = strrchr(pv->pv_name+1,'/'); + if ( pv_name == 0) pv_name = pv->pv_name; + else pv_name++; + sz = sprintf(buf, + "[%c%c] %-21s %8d /%-6d " + "%8d /%-6d %8d /%-6d", + inactive_flag, + allocation_flag, + pv_name, + pv->pe_total * pv->pe_size >> 1, + pv->pe_total, + pv->pe_allocated * pv->pe_size >> 1, + pv->pe_allocated, + (pv->pe_total - pv->pe_allocated) * + pv->pe_size >> 1, + pv->pe_total - pv->pe_allocated); + return sz; +} + +static void _show_uuid(const char *src, char *b, char *e) { + int i; + + e--; + for(i = 0; *src && (b != e); i++) { + if(i && !(i & 0x3)) + *b++ = '-'; + *b++ = *src++; + } + *b = '\0'; +} --- linux/drivers/md/Makefile.orig Fri Feb 16 13:56:28 2001 +++ linux/drivers/md/Makefile Fri Feb 16 13:56:39 2001 @@ -6,7 +6,7 @@ export-objs := md.o xor.o list-multi := lvm-mod.o -lvm-mod-objs := lvm.o lvm-snap.o +lvm-mod-objs := lvm.o lvm-snap.o lvm-fs.o # Note: link order is important. All raid personalities # and xor.o must come before md.o, as they each initialise diff -ruN -X /home/joe/packages/dontdiff linux_2.4.1/drivers/md/lvm-snap.h linux/drivers/md/lvm-snap.h --- linux_2.4.1/drivers/md/lvm-snap.h Fri Feb 16 14:51:26 2001 +++ linux/drivers/md/lvm-snap.h Thu Jan 1 01:00:00 1970 @@ -1,47 +0,0 @@ -/* - * kernel/lvm-snap.h - * - * Copyright (C) 2001 Sistina Software - * - * - * LVM driver is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * LVM driver is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with GNU CC; see the file COPYING. If not, write to - * the Free Software Foundation, 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - * - */ - -/* - * Changelog - * - * 05/01/2001:Joe Thornber - Factored this file out of lvm.c - * - */ - -#ifndef LVM_SNAP_H -#define LVM_SNAP_H - -/* external snapshot calls */ -extern inline int lvm_get_blksize(kdev_t); -extern int lvm_snapshot_alloc(lv_t *); -extern void lvm_snapshot_fill_COW_page(vg_t *, lv_t *); -extern int lvm_snapshot_COW(kdev_t, ulong, ulong, ulong, lv_t *); -extern int lvm_snapshot_remap_block(kdev_t *, ulong *, ulong, lv_t *); -extern void lvm_snapshot_release(lv_t *); -extern int lvm_write_COW_table_block(vg_t *, lv_t *); -extern inline void lvm_hash_link(lv_block_exception_t *, - kdev_t, ulong, lv_t *); -extern int lvm_snapshot_alloc_hash_table(lv_t *); -extern void lvm_drop_snapshot(lv_t *, const char *); - -#endif --- linux/Documentation/ioctl-number.txt.orig Mon Mar 12 10:16:51 2001 +++ linux/Documentation/ioctl-number.txt Mon Mar 12 10:17:36 2001 @@ -186,3 +186,6 @@ 0xB1 00-1F PPPoX 0xCB 00-1F CBM serial IEC bus in development: + +0xFE 00-9F Logical Volume Manager +