lvm.c 75.6 KB
Newer Older
1
#error Broken until maintainers will sanitize kdev_t handling
Linus Torvalds's avatar
Linus Torvalds committed
2 3 4 5 6 7 8 9 10
/*
 * kernel/lvm.c
 *
 * Copyright (C) 1997 - 2000  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
Linus Torvalds's avatar
Linus Torvalds committed
11
 * January 2001
Linus Torvalds's avatar
Linus Torvalds committed
12 13 14 15 16 17
 *
 *
 * 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.
Linus Torvalds's avatar
Linus Torvalds committed
18
 *
Linus Torvalds's avatar
Linus Torvalds committed
19 20 21 22
 * 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.
Linus Torvalds's avatar
Linus Torvalds committed
23
 *
Linus Torvalds's avatar
Linus Torvalds committed
24 25 26
 * 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,
Linus Torvalds's avatar
Linus Torvalds committed
27
 * Boston, MA 02111-1307, USA.
Linus Torvalds's avatar
Linus Torvalds committed
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
 *
 */

/*
 * Changelog
 *
 *    09/11/1997 - added chr ioctls VG_STATUS_GET_COUNT
 *                 and VG_STATUS_GET_NAMELIST
 *    18/01/1998 - change lvm_chr_open/close lock handling
 *    30/04/1998 - changed LV_STATUS ioctl to LV_STATUS_BYNAME and
 *               - added   LV_STATUS_BYINDEX ioctl
 *               - used lvm_status_byname_req_t and
 *                      lvm_status_byindex_req_t vars
 *    04/05/1998 - added multiple device support
 *    08/05/1998 - added support to set/clear extendable flag in volume group
 *    09/05/1998 - changed output of lvm_proc_get_global_info() because of
 *                 support for free (eg. longer) logical volume names
 *    12/05/1998 - added spin_locks (thanks to Pascal van Dam
 *                 <pascal@ramoth.xs4all.nl>)
 *    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
 *               - #ifdef blocked spin_lock calls to avoid compile errors
 *                 with 2.0.x
 *    11/06/1998 - another enhancement to spinlock code in lvm_chr_open()
 *                 and use of LVM_VERSION_CODE instead of my own macros
 *                 (thanks to  Michael Marxmeier <mike@msede.com>)
 *    07/07/1998 - added statistics in lvm_map()
 *    08/07/1998 - saved statistics in lvm_do_lv_extend_reduce()
 *    25/07/1998 - used __initfunc macro
 *    02/08/1998 - changes for official char/block major numbers
 *    07/08/1998 - avoided init_module() and cleanup_module() to be static
 *    30/08/1998 - changed VG lv_open counter from sum of LV lv_open counters
 *                 to sum of LVs open (no matter how often each is)
 *    01/09/1998 - fixed lvm_gendisk.part[] index error
 *    07/09/1998 - added copying of lv_current_pe-array
 *                 in LV_STATUS_BYINDEX ioctl
 *    17/11/1998 - added KERN_* levels to printk
 *    13/01/1999 - fixed LV index bug in lvm_do_lv_create() which hit lvrename
 *    07/02/1999 - fixed spinlock handling bug in case of LVM_RESET
 *                 by moving spinlock code from lvm_chr_open()
 *                 to lvm_chr_ioctl()
 *               - added LVM_LOCK_LVM ioctl to lvm_chr_ioctl()
 *               - allowed LVM_RESET and retrieval commands to go ahead;
 *                 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()
 *                 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
 *               - implemented LV read_ahead handling with lvm_blk_read()
 *                 and lvm_blk_write()
 *    10/02/1999 - implemented 2.[12].* support function lvm_hd_name()
 *                 to be used in drivers/block/genhd.c by disk_name()
 *    12/02/1999 - fixed index bug in lvm_blk_ioctl(), HDIO_GETGEO
 *               - enhanced gendisk insert/remove handling
 *    16/02/1999 - changed to dynamic block minor number allocation to
 *                 have as much as 99 volume groups with 256 logical volumes
 *                 as the grand total; this allows having 1 volume group with
 *                 up to 256 logical volumes in it
 *    21/02/1999 - added LV open count information to proc filesystem
 *               - substituted redundant LVM_RESET code by calls
 *                 to lvm_do_vg_remove()
 *    22/02/1999 - used schedule_timeout() to be more responsive
 *                 in case of lvm_do_vg_remove() with lots of logical volumes
 *    19/03/1999 - fixed NULL pointer bug in module_init/lvm_init
 *    17/05/1999 - used DECLARE_WAIT_QUEUE_HEAD macro (>2.3.0)
 *               - enhanced lvm_hd_name support
 *    03/07/1999 - avoided use of KERNEL_VERSION macro based ifdefs and
 *                 memcpy_tofs/memcpy_fromfs macro redefinitions
 *    06/07/1999 - corrected reads/writes statistic counter copy in case
 *                 of striped logical volume
 *    28/07/1999 - implemented snapshot logical volumes
 *                 - lvm_chr_ioctl
 *                   - LV_STATUS_BYINDEX
 *                   - LV_STATUS_BYNAME
 *                 - lvm_do_lv_create
 *                 - lvm_do_lv_remove
 *                 - lvm_map
 *                 - new lvm_snapshot_remap_block
 *                 - new lvm_snapshot_remap_new_block
 *    08/10/1999 - implemented support for multiple snapshots per
 *                 original logical volume
 *    12/10/1999 - support for 2.3.19
 *    11/11/1999 - support for 2.3.28
 *    21/11/1999 - changed lvm_map() interface to buffer_head based
 *    19/12/1999 - support for 2.3.33
 *    01/01/2000 - changed locking concept in lvm_map(),
 *                 lvm_do_vg_create() and lvm_do_lv_remove()
 *    15/01/2000 - fixed PV_FLUSH bug in lvm_chr_ioctl()
 *    24/01/2000 - ported to 2.3.40 including Alan Cox's pointer changes etc.
 *    29/01/2000 - used kmalloc/kfree again for all small structures
 *    20/01/2000 - cleaned up lvm_chr_ioctl by moving code
 *                 to seperated functions
 *               - avoided "/dev/" in proc filesystem output
 *               - avoided inline strings functions lvm_strlen etc.
 *    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
 *    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
 *                 getting an lv_t based on the dev_t of the Logical Volume
 *    14/09/2000 - enhanced lvm_do_lv_create to upcall VFS functions
 *                 to sync and lock, activate snapshot and unlock the FS
 *                 (to support journaled filesystems)
 *    18/09/2000 - hardsector size support
 *    27/09/2000 - implemented lvm_do_lv_rename() and lvm_do_vg_rename()
 *    30/10/2000 - added Andi Kleen's LV_BMAP ioctl to support LILO
 *    01/11/2000 - added memory information on hash tables to
 *                 lvm_proc_get_global_info()
 *    02/11/2000 - implemented /proc/lvm/ hierarchy
Linus Torvalds's avatar
Linus Torvalds committed
143 144 145 146 147 148 149 150
 *    22/11/2000 - changed lvm_do_create_proc_entry_of_pv () to work
 *                 with devfs
 *    26/11/2000 - corrected #ifdef locations for PROC_FS
 *    28/11/2000 - fixed lvm_do_vg_extend() NULL pointer BUG
 *               - fixed lvm_do_create_proc_entry_of_pv() buffer tampering BUG
 *    08/01/2001 - Removed conditional compiles related to PROC_FS,
 *                 procfs is always supported now. (JT)
 *    12/01/2001 - avoided flushing logical volume in case of shrinking
Linus Torvalds's avatar
Linus Torvalds committed
151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177
 *                 because of unnecessary overhead in case of heavy updates
 *    25/01/2001 - Allow RO open of an inactive LV so it can be reactivated.
 *    31/01/2001 - If you try and BMAP a snapshot you now get an -EPERM
 *    01/02/2001 - factored __remap_snapshot out of lvm_map
 *    12/02/2001 - move devfs code to create VG before LVs
 *    14/02/2001 - tidied device defines for blk.h
 *               - tidied debug statements
 *               - more lvm_map tidying
 *    14/02/2001 - bug: vg[] member not set back to NULL if activation fails
 *    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.
 *    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 - 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.
Linus Torvalds's avatar
Linus Torvalds committed
178 179 180
 *    05/04/2001 - lvm_map bugs: don't use b_blocknr/b_dev in lvm_map, it
 *		   destroys stacking devices. call b_end_io on failed maps.
 *		   (Jens Axboe)
Linus Torvalds's avatar
Linus Torvalds committed
181 182
 *               - Defer writes to an extent that is being moved [JT + AD]
 *    28/05/2001 - implemented missing BLKSSZGET ioctl [AD]
Linus Torvalds's avatar
Linus Torvalds committed
183 184 185
 *    28/12/2001 - buffer_head -> bio
 *                 removed huge allocation of a lv_t on stack
 *                 (Anders Gustafsson)
Dave Jones's avatar
Dave Jones committed
186 187 188
 *    07/01/2002 - fixed sizeof(lv_t) differences in user/kernel-space
 *                 removed another huge allocation of a lv_t on stack
 *                 (Anders Gustafsson)
Linus Torvalds's avatar
Linus Torvalds committed
189 190 191 192
 *
 */


Linus Torvalds's avatar
Linus Torvalds committed
193 194 195
#define MAJOR_NR LVM_BLK_MAJOR
#define DEVICE_OFF(device)
#define LOCAL_END_REQUEST
Linus Torvalds's avatar
Linus Torvalds committed
196 197 198 199 200

/* lvm_do_lv_create calls fsync_dev_lockfs()/unlockfs() */
/* #define	LVM_VFS_ENHANCEMENT */

#include <linux/config.h>
Linus Torvalds's avatar
Linus Torvalds committed
201

Linus Torvalds's avatar
Linus Torvalds committed
202 203 204 205 206 207 208 209 210 211
#include <linux/module.h>

#include <linux/kernel.h>
#include <linux/vmalloc.h>
#include <linux/slab.h>
#include <linux/init.h>

#include <linux/hdreg.h>
#include <linux/stat.h>
#include <linux/fs.h>
212
#include <linux/bio.h>
Linus Torvalds's avatar
Linus Torvalds committed
213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229
#include <linux/proc_fs.h>
#include <linux/blkdev.h>
#include <linux/genhd.h>
#include <linux/smp_lock.h>
#include <asm/ioctl.h>
#include <asm/uaccess.h>

#ifdef CONFIG_KERNELD
#include <linux/kerneld.h>
#endif

#include <linux/blk.h>
#include <linux/blkpg.h>

#include <linux/errno.h>
#include <linux/lvm.h>

Linus Torvalds's avatar
Linus Torvalds committed
230
#include "lvm-internal.h"
Linus Torvalds's avatar
Linus Torvalds committed
231

Linus Torvalds's avatar
Linus Torvalds committed
232 233 234 235 236
#ifndef WRITEA
#  define WRITEA WRITE
#endif


Linus Torvalds's avatar
Linus Torvalds committed
237 238 239
/*
 * External function prototypes
 */
Linus Torvalds's avatar
Linus Torvalds committed
240
static int lvm_make_request_fn(request_queue_t*, struct bio *);
Linus Torvalds's avatar
Linus Torvalds committed
241 242 243 244 245

static int lvm_blk_ioctl(struct inode *, struct file *, uint, ulong);
static int lvm_blk_open(struct inode *, struct file *);

static int lvm_blk_close(struct inode *, struct file *);
Linus Torvalds's avatar
Linus Torvalds committed
246
static int lvm_get_snapshot_use_rate(lv_t *lv_ptr, void *arg);
Linus Torvalds's avatar
Linus Torvalds committed
247 248
static int lvm_user_bmap(struct inode *, struct lv_bmap *);

Linus Torvalds's avatar
Linus Torvalds committed
249 250
static int lvm_chr_open(struct inode *, struct file *);
static int lvm_chr_close(struct inode *, struct file *);
Linus Torvalds's avatar
Linus Torvalds committed
251 252 253 254 255 256 257 258 259
static int lvm_chr_ioctl(struct inode *, struct file *, uint, ulong);


/* End external function prototypes */


/*
 * Internal function prototypes
 */
Linus Torvalds's avatar
Linus Torvalds committed
260
static void lvm_cleanup(void);
Linus Torvalds's avatar
Linus Torvalds committed
261 262 263 264 265
static void lvm_init_vars(void);

#ifdef LVM_HD_NAME
extern void (*lvm_hd_name_ptr) (char *, int);
#endif
Linus Torvalds's avatar
Linus Torvalds committed
266
static int lvm_map(struct bio *);
Linus Torvalds's avatar
Linus Torvalds committed
267 268 269 270 271
static int lvm_do_lock_lvm(void);
static int lvm_do_le_remap(vg_t *, void *);

static int lvm_do_pv_create(pv_t *, vg_t *, ulong);
static int lvm_do_pv_remove(vg_t *, ulong);
Dave Jones's avatar
Dave Jones committed
272 273
static int lvm_do_lv_create(int, char *, userlv_t *);
static int lvm_do_lv_extend_reduce(int, char *, userlv_t *);
Linus Torvalds's avatar
Linus Torvalds committed
274
static int lvm_do_lv_remove(int, char *, int);
Dave Jones's avatar
Dave Jones committed
275
static int lvm_do_lv_rename(vg_t *, lv_req_t *, userlv_t *);
Linus Torvalds's avatar
Linus Torvalds committed
276 277 278 279 280 281 282 283
static int lvm_do_lv_status_byname(vg_t *r, void *);
static int lvm_do_lv_status_byindex(vg_t *, void *);
static int lvm_do_lv_status_bydev(vg_t *, void *);

static int lvm_do_pe_lock_unlock(vg_t *r, void *);

static int lvm_do_pv_change(vg_t*, void*);
static int lvm_do_pv_status(vg_t *, void *);
Linus Torvalds's avatar
Linus Torvalds committed
284
static int lvm_do_pv_flush(void *);
Linus Torvalds's avatar
Linus Torvalds committed
285

Linus Torvalds's avatar
Linus Torvalds committed
286
static int lvm_do_vg_create(void *, int minor);
Linus Torvalds's avatar
Linus Torvalds committed
287 288 289 290 291
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 *);
Linus Torvalds's avatar
Linus Torvalds committed
292 293 294
static void __update_hardsectsize(lv_t *lv);


Linus Torvalds's avatar
Linus Torvalds committed
295 296 297
static void _queue_io(struct bio *bh, int rw);
static struct bio *_dequeue_io(void);
static void _flush_io(struct bio *bh);
Linus Torvalds's avatar
Linus Torvalds committed
298 299 300 301 302 303

static int _open_pv(pv_t *pv);
static void _close_pv(pv_t *pv);

static unsigned long _sectors_to_k(unsigned long sect);

Linus Torvalds's avatar
Linus Torvalds committed
304 305
#ifdef LVM_HD_NAME
void lvm_hd_name(char *, int);
Linus Torvalds's avatar
Linus Torvalds committed
306 307 308 309
#endif
/* END Internal function prototypes */


Linus Torvalds's avatar
Linus Torvalds committed
310 311 312 313 314
/* variables */
char *lvm_version = "LVM 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;
Linus Torvalds's avatar
Linus Torvalds committed
315 316


Linus Torvalds's avatar
Linus Torvalds committed
317 318
/* volume group descriptor area pointers */
vg_t *vg[ABS_MAX_VG];
Linus Torvalds's avatar
Linus Torvalds committed
319 320 321 322 323 324 325 326 327 328 329 330

/* map from block minor number to VG and LV numbers */
typedef struct {
	int vg_number;
	int lv_number;
} vg_lv_map_t;
static vg_lv_map_t vg_lv_map[ABS_MAX_LV];


/* Request structures (lvm_chr_ioctl()) */
static pv_change_req_t pv_change_req;
static pv_status_req_t pv_status_req;
Linus Torvalds's avatar
Linus Torvalds committed
331
volatile static pe_lock_req_t pe_lock_req;
Linus Torvalds's avatar
Linus Torvalds committed
332 333 334 335 336 337 338 339 340 341
static le_remap_req_t le_remap_req;
static lv_req_t lv_req;

#ifdef LVM_TOTAL_RESET
static int lvm_reset_spindown = 0;
#endif

static char pv_name[NAME_LEN];
/* static char rootvg[NAME_LEN] = { 0, }; */
static int lock = 0;
Linus Torvalds's avatar
Linus Torvalds committed
342
static int _lock_open_count = 0;
Linus Torvalds's avatar
Linus Torvalds committed
343 344 345 346 347 348 349
static uint vg_count = 0;
static long lvm_chr_open_count = 0;
static DECLARE_WAIT_QUEUE_HEAD(lvm_wait);

static spinlock_t lvm_lock = SPIN_LOCK_UNLOCKED;
static spinlock_t lvm_snapshot_lock = SPIN_LOCK_UNLOCKED;

Linus Torvalds's avatar
Linus Torvalds committed
350
static struct bio *_pe_requests;
Linus Torvalds's avatar
Linus Torvalds committed
351
static DECLARE_RWSEM(_pe_lock);
Linus Torvalds's avatar
Linus Torvalds committed
352

Linus Torvalds's avatar
Linus Torvalds committed
353 354

struct file_operations lvm_chr_fops = {
Linus Torvalds's avatar
Linus Torvalds committed
355 356 357 358 359 360
	open:		lvm_chr_open,
	release:	lvm_chr_close,
	ioctl:		lvm_chr_ioctl,
};

/* block device operations structure needed for 2.3.38? and above */
Linus Torvalds's avatar
Linus Torvalds committed
361
struct block_device_operations lvm_blk_dops =
Linus Torvalds's avatar
Linus Torvalds committed
362
{
Linus Torvalds's avatar
Linus Torvalds committed
363
	owner:		THIS_MODULE,
Linus Torvalds's avatar
Linus Torvalds committed
364 365 366 367 368 369 370 371
	open: 		lvm_blk_open,
	release:	lvm_blk_close,
	ioctl:		lvm_blk_ioctl,
};


/* gendisk structures */
static struct hd_struct lvm_hd_struct[MAX_LV];
Linus Torvalds's avatar
Linus Torvalds committed
372 373 374
static int lvm_blocksizes[MAX_LV];
static int lvm_size[MAX_LV];

Linus Torvalds's avatar
Linus Torvalds committed
375 376
static struct gendisk lvm_gendisk =
{
Linus Torvalds's avatar
Linus Torvalds committed
377 378 379 380 381 382
	major:		MAJOR_NR,
	major_name:	LVM_NAME,
	minor_shift:	0,
	part:		lvm_hd_struct,
	sizes:		lvm_size,
	nr_real:	MAX_LV,
Linus Torvalds's avatar
Linus Torvalds committed
383 384 385 386 387
};

/*
 * Driver initialization...
 */
Linus Torvalds's avatar
Linus Torvalds committed
388
int lvm_init(void)
Linus Torvalds's avatar
Linus Torvalds committed
389
{
390
	if (register_chrdev(LVM_CHAR_MAJOR,
Linus Torvalds's avatar
Linus Torvalds committed
391
				  lvm_name, &lvm_chr_fops) < 0) {
392
		printk(KERN_ERR "%s -- register_chrdev failed\n",
Linus Torvalds's avatar
Linus Torvalds committed
393
		       lvm_name);
Linus Torvalds's avatar
Linus Torvalds committed
394 395
		return -EIO;
	}
Linus Torvalds's avatar
Linus Torvalds committed
396

397 398
	if (register_blkdev(MAJOR_NR, lvm_name, &lvm_blk_dops) < 0) {
		printk("%s -- register_blkdev failed\n", lvm_name);
399
		if (unregister_chrdev(LVM_CHAR_MAJOR, lvm_name) < 0)
Linus Torvalds's avatar
Linus Torvalds committed
400
			printk(KERN_ERR
401
			       "%s -- unregister_chrdev failed\n",
Linus Torvalds's avatar
Linus Torvalds committed
402
			       lvm_name);
Linus Torvalds's avatar
Linus Torvalds committed
403 404 405
		return -EIO;
	}

Linus Torvalds's avatar
Linus Torvalds committed
406
	lvm_init_fs();
Linus Torvalds's avatar
Linus Torvalds committed
407 408 409
	lvm_init_vars();
	lvm_geninit(&lvm_gendisk);

Linus Torvalds's avatar
Linus Torvalds committed
410
	add_gendisk(&lvm_gendisk);
Linus Torvalds's avatar
Linus Torvalds committed
411 412 413 414 415 416 417 418

#ifdef LVM_HD_NAME
	/* reference from drivers/block/genhd.c */
	lvm_hd_name_ptr = lvm_hd_name;
#endif

	blk_queue_make_request(BLK_DEFAULT_QUEUE(MAJOR_NR), lvm_make_request_fn);

Linus Torvalds's avatar
Linus Torvalds committed
419

Linus Torvalds's avatar
Linus Torvalds committed
420 421 422
	/* initialise the pe lock */
	pe_lock_req.lock = UNLOCK_PE;

Linus Torvalds's avatar
Linus Torvalds committed
423 424 425 426 427 428
	/* optional read root VGDA */
/*
   if ( *rootvg != 0) vg_read_with_pv_and_lv ( rootvg, &vg);
*/

#ifdef MODULE
Linus Torvalds's avatar
Linus Torvalds committed
429
	printk(KERN_INFO "%s module loaded\n", lvm_version);
Linus Torvalds's avatar
Linus Torvalds committed
430
#else
Linus Torvalds's avatar
Linus Torvalds committed
431
	printk(KERN_INFO "%s\n", lvm_version);
Linus Torvalds's avatar
Linus Torvalds committed
432 433 434
#endif

	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
435
} /* lvm_init() */
Linus Torvalds's avatar
Linus Torvalds committed
436 437 438


/*
Linus Torvalds's avatar
Linus Torvalds committed
439
 * cleanup...
Linus Torvalds's avatar
Linus Torvalds committed
440
 */
Linus Torvalds's avatar
Linus Torvalds committed
441
static void lvm_cleanup(void)
Linus Torvalds's avatar
Linus Torvalds committed
442
{
443 444
	if (unregister_chrdev(LVM_CHAR_MAJOR, lvm_name) < 0)
		printk(KERN_ERR "%s -- unregister_chrdev failed\n",
Linus Torvalds's avatar
Linus Torvalds committed
445
		       lvm_name);
446 447
	if (unregister_blkdev(MAJOR_NR, lvm_name) < 0)
		printk(KERN_ERR "%s -- unregister_blkdev failed\n",
Linus Torvalds's avatar
Linus Torvalds committed
448
		       lvm_name);
Linus Torvalds's avatar
Linus Torvalds committed
449

Linus Torvalds's avatar
Linus Torvalds committed
450
	del_gendisk(&lvm_gendisk);
Linus Torvalds's avatar
Linus Torvalds committed
451

Linus Torvalds's avatar
Linus Torvalds committed
452
	blk_clear(MAJOR_NR);
Linus Torvalds's avatar
Linus Torvalds committed
453 454 455 456 457 458

#ifdef LVM_HD_NAME
	/* reference from linux/drivers/block/genhd.c */
	lvm_hd_name_ptr = NULL;
#endif

Linus Torvalds's avatar
Linus Torvalds committed
459 460 461 462
	/* unregister with procfs and devfs */
	lvm_fin_fs();

#ifdef MODULE
Linus Torvalds's avatar
Linus Torvalds committed
463
	printk(KERN_INFO "%s -- Module successfully deactivated\n", lvm_name);
Linus Torvalds's avatar
Linus Torvalds committed
464
#endif
Linus Torvalds's avatar
Linus Torvalds committed
465 466

	return;
Linus Torvalds's avatar
Linus Torvalds committed
467
}	/* lvm_cleanup() */
Linus Torvalds's avatar
Linus Torvalds committed
468 469 470 471

/*
 * support function to initialize lvm variables
 */
Linus Torvalds's avatar
Linus Torvalds committed
472
static void __init lvm_init_vars(void)
Linus Torvalds's avatar
Linus Torvalds committed
473 474 475 476 477 478 479 480
{
	int v;

	loadtime = CURRENT_TIME;

	lvm_lock = lvm_snapshot_lock = SPIN_LOCK_UNLOCKED;

	pe_lock_req.lock = UNLOCK_PE;
Linus Torvalds's avatar
Linus Torvalds committed
481 482
	pe_lock_req.data.lv_dev = NODEV;
	pe_lock_req.data.pv_dev = NODEV;
Linus Torvalds's avatar
Linus Torvalds committed
483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504
	pe_lock_req.data.pv_offset = 0;

	/* Initialize VG pointers */
	for (v = 0; v < ABS_MAX_VG; v++) vg[v] = NULL;

	/* Initialize LV -> VG association */
	for (v = 0; v < ABS_MAX_LV; v++) {
		/* index ABS_MAX_VG never used for real VG */
		vg_lv_map[v].vg_number = ABS_MAX_VG;
		vg_lv_map[v].lv_number = -1;
	}

	return;
} /* lvm_init_vars() */


/********************************************************************
 *
 * Character device functions
 *
 ********************************************************************/

Linus Torvalds's avatar
Linus Torvalds committed
505 506 507
#define MODE_TO_STR(mode) (mode) & FMODE_READ ? "READ" : "", \
			  (mode) & FMODE_WRITE ? "WRITE" : ""

Linus Torvalds's avatar
Linus Torvalds committed
508 509 510
/*
 * character device open routine
 */
Linus Torvalds's avatar
Linus Torvalds committed
511
static int lvm_chr_open(struct inode *inode, struct file *file)
Linus Torvalds's avatar
Linus Torvalds committed
512
{
Linus Torvalds's avatar
Linus Torvalds committed
513
	unsigned int minor = minor(inode->i_rdev);
Linus Torvalds's avatar
Linus Torvalds committed
514

Linus Torvalds's avatar
Linus Torvalds committed
515 516
	P_DEV("chr_open MINOR: %d  VG#: %d  mode: %s%s  lock: %d\n",
	      minor, VG_CHR(minor), MODE_TO_STR(file->f_mode), lock);
Linus Torvalds's avatar
Linus Torvalds committed
517 518 519 520 521 522 523

	/* super user validation */
	if (!capable(CAP_SYS_ADMIN)) return -EACCES;

	/* Group special file open */
	if (VG_CHR(minor) > MAX_VG) return -ENXIO;

Linus Torvalds's avatar
Linus Torvalds committed
524 525 526 527 528
       spin_lock(&lvm_lock);
       if(lock == current->pid)
               _lock_open_count++;
       spin_unlock(&lvm_lock);

Linus Torvalds's avatar
Linus Torvalds committed
529 530
	lvm_chr_open_count++;

Linus Torvalds's avatar
Linus Torvalds committed
531 532
	MOD_INC_USE_COUNT;

Linus Torvalds's avatar
Linus Torvalds committed
533 534 535 536 537 538 539 540 541 542 543 544
	return 0;
} /* lvm_chr_open() */


/*
 * character device i/o-control routine
 *
 * Only one changing process can do changing ioctl at one time,
 * others will block.
 *
 */
static int lvm_chr_ioctl(struct inode *inode, struct file *file,
Linus Torvalds's avatar
Linus Torvalds committed
545
		  uint command, ulong a)
Linus Torvalds's avatar
Linus Torvalds committed
546
{
Linus Torvalds's avatar
Linus Torvalds committed
547
	int minor = minor(inode->i_rdev);
Linus Torvalds's avatar
Linus Torvalds committed
548 549
	uint extendable, l, v;
	void *arg = (void *) a;
Dave Jones's avatar
Dave Jones committed
550
	userlv_t ulv;
Linus Torvalds's avatar
Linus Torvalds committed
551 552 553 554 555
	vg_t* vg_ptr = vg[VG_CHR(minor)];

	/* otherwise cc will complain about unused variables */
	(void) lvm_lock;

Linus Torvalds's avatar
Linus Torvalds committed
556 557
	P_IOCTL("chr MINOR: %d  command: 0x%X  arg: %p  VG#: %d  mode: %s%s\n",
		minor, command, arg, VG_CHR(minor), MODE_TO_STR(file->f_mode));
Linus Torvalds's avatar
Linus Torvalds committed
558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604

#ifdef LVM_TOTAL_RESET
	if (lvm_reset_spindown > 0) return -EACCES;
#endif

	/* Main command switch */
	switch (command) {
	case LVM_LOCK_LVM:
		/* lock the LVM */
		return lvm_do_lock_lvm();

	case LVM_GET_IOP_VERSION:
		/* check lvm version to ensure driver/tools+lib
		   interoperability */
		if (copy_to_user(arg, &lvm_iop_version, sizeof(ushort)) != 0)
			return -EFAULT;
		return 0;

#ifdef LVM_TOTAL_RESET
	case LVM_RESET:
		/* lock reset function */
		lvm_reset_spindown = 1;
		for (v = 0; v < ABS_MAX_VG; v++) {
			if (vg[v] != NULL) lvm_do_vg_remove(v);
		}

#ifdef MODULE
		while (GET_USE_COUNT(&__this_module) < 1)
			MOD_INC_USE_COUNT;
		while (GET_USE_COUNT(&__this_module) > 1)
			MOD_DEC_USE_COUNT;
#endif /* MODULE */
		lock = 0;	/* release lock */
		wake_up_interruptible(&lvm_wait);
		return 0;
#endif /* LVM_TOTAL_RESET */


	case LE_REMAP:
		/* remap a logical extent (after moving the physical extent) */
		return lvm_do_le_remap(vg_ptr,arg);

	case PE_LOCK_UNLOCK:
		/* lock/unlock i/o to a physical extent to move it to another
		   physical volume (move's done in user space's pvmove) */
		return lvm_do_pe_lock_unlock(vg_ptr,arg);

Linus Torvalds's avatar
Linus Torvalds committed
605
	case VG_CREATE_OLD:
Linus Torvalds's avatar
Linus Torvalds committed
606
		/* create a VGDA */
Linus Torvalds's avatar
Linus Torvalds committed
607 608 609 610 611
		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);
Linus Torvalds's avatar
Linus Torvalds committed
612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661

	case VG_EXTEND:
		/* extend a volume group */
		return lvm_do_vg_extend(vg_ptr, arg);

	case VG_REDUCE:
		/* reduce a volume group */
		return lvm_do_vg_reduce(vg_ptr, arg);

	case VG_RENAME:
		/* rename a volume group */
		return lvm_do_vg_rename(vg_ptr, arg);

	case VG_REMOVE:
		/* remove an inactive VGDA */
		return lvm_do_vg_remove(minor);


	case VG_SET_EXTENDABLE:
		/* set/clear extendability flag of volume group */
		if (vg_ptr == NULL) return -ENXIO;
		if (copy_from_user(&extendable, arg, sizeof(extendable)) != 0)
			return -EFAULT;

		if (extendable == VG_EXTENDABLE ||
		    extendable == ~VG_EXTENDABLE) {
			if (extendable == VG_EXTENDABLE)
				vg_ptr->vg_status |= VG_EXTENDABLE;
			else
				vg_ptr->vg_status &= ~VG_EXTENDABLE;
		} else return -EINVAL;
		return 0;


	case VG_STATUS:
		/* get volume group data (only the vg_t struct) */
		if (vg_ptr == NULL) return -ENXIO;
		if (copy_to_user(arg, vg_ptr, sizeof(vg_t)) != 0)
			return -EFAULT;
		return 0;


	case VG_STATUS_GET_COUNT:
		/* get volume group count */
		if (copy_to_user(arg, &vg_count, sizeof(vg_count)) != 0)
			return -EFAULT;
		return 0;


	case VG_STATUS_GET_NAMELIST:
Linus Torvalds's avatar
Linus Torvalds committed
662
		/* get volume group names */
Linus Torvalds's avatar
Linus Torvalds committed
663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685
		for (l = v = 0; v < ABS_MAX_VG; v++) {
			if (vg[v] != NULL) {
				if (copy_to_user(arg + l * NAME_LEN,
						 vg[v]->vg_name,
						 NAME_LEN) != 0)
					return -EFAULT;
				l++;
			}
		}
		return 0;


	case LV_CREATE:
	case LV_EXTEND:
	case LV_REDUCE:
	case LV_REMOVE:
	case LV_RENAME:
		/* create, extend, reduce, remove or rename a logical volume */
		if (vg_ptr == NULL) return -ENXIO;
		if (copy_from_user(&lv_req, arg, sizeof(lv_req)) != 0)
			return -EFAULT;

		if (command != LV_REMOVE) {
Dave Jones's avatar
Dave Jones committed
686
			if (copy_from_user(&ulv, lv_req.lv, sizeof(userlv_t)) != 0)
Linus Torvalds's avatar
Linus Torvalds committed
687 688 689 690
				return -EFAULT;
		}
		switch (command) {
		case LV_CREATE:
Dave Jones's avatar
Dave Jones committed
691
			return lvm_do_lv_create(minor, lv_req.lv_name, &ulv);
Linus Torvalds's avatar
Linus Torvalds committed
692 693 694

		case LV_EXTEND:
		case LV_REDUCE:
Dave Jones's avatar
Dave Jones committed
695
			return lvm_do_lv_extend_reduce(minor, lv_req.lv_name, &ulv);
Linus Torvalds's avatar
Linus Torvalds committed
696 697 698 699
		case LV_REMOVE:
			return lvm_do_lv_remove(minor, lv_req.lv_name, -1);

		case LV_RENAME:
Dave Jones's avatar
Dave Jones committed
700
			return lvm_do_lv_rename(vg_ptr, &lv_req, &ulv);
Linus Torvalds's avatar
Linus Torvalds committed
701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716
		}




	case LV_STATUS_BYNAME:
		/* get status of a logical volume by name */
		return lvm_do_lv_status_byname(vg_ptr, arg);


	case LV_STATUS_BYINDEX:
		/* get status of a logical volume by index */
		return lvm_do_lv_status_byindex(vg_ptr, arg);


	case LV_STATUS_BYDEV:
Linus Torvalds's avatar
Linus Torvalds committed
717
		/* get status of a logical volume by device */
Linus Torvalds's avatar
Linus Torvalds committed
718 719 720 721 722 723 724 725 726 727 728 729 730 731 732
		return lvm_do_lv_status_bydev(vg_ptr, arg);


	case PV_CHANGE:
		/* change a physical volume */
		return lvm_do_pv_change(vg_ptr,arg);


	case PV_STATUS:
		/* get physical volume data (pv_t structure only) */
		return lvm_do_pv_status(vg_ptr,arg);


	case PV_FLUSH:
		/* physical volume buffer flush/invalidate */
Linus Torvalds's avatar
Linus Torvalds committed
733
               return lvm_do_pv_flush(arg);
Linus Torvalds's avatar
Linus Torvalds committed
734 735 736 737


	default:
		printk(KERN_WARNING
Linus Torvalds's avatar
Linus Torvalds committed
738
		       "%s -- lvm_chr_ioctl: unknown command 0x%x\n",
Linus Torvalds's avatar
Linus Torvalds committed
739 740 741 742 743 744 745 746 747 748 749 750 751
		       lvm_name, command);
		return -EINVAL;
	}

	return 0;
} /* lvm_chr_ioctl */


/*
 * character device close routine
 */
static int lvm_chr_close(struct inode *inode, struct file *file)
{
Linus Torvalds's avatar
Linus Torvalds committed
752
	P_DEV("chr_close MINOR: %d  VG#: %d\n",
Linus Torvalds's avatar
Linus Torvalds committed
753
	      minor(inode->i_rdev), VG_CHR(minor(inode->i_rdev)));
Linus Torvalds's avatar
Linus Torvalds committed
754 755 756 757 758 759 760 761 762

#ifdef LVM_TOTAL_RESET
	if (lvm_reset_spindown > 0) {
		lvm_reset_spindown = 0;
		lvm_chr_open_count = 0;
	}
#endif

	if (lvm_chr_open_count > 0) lvm_chr_open_count--;
Linus Torvalds's avatar
Linus Torvalds committed
763 764 765 766 767 768 769 770 771

       spin_lock(&lvm_lock);
       if(lock == current->pid) {
               if(!_lock_open_count) {
			P_DEV("chr_close: unlocking LVM for pid %d\n", lock);
                       lock = 0;
                       wake_up_interruptible(&lvm_wait);
               } else
                       _lock_open_count--;
Linus Torvalds's avatar
Linus Torvalds committed
772
	}
Linus Torvalds's avatar
Linus Torvalds committed
773 774 775
       spin_unlock(&lvm_lock);

	MOD_DEC_USE_COUNT;
Linus Torvalds's avatar
Linus Torvalds committed
776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792

	return 0;
} /* lvm_chr_close() */



/********************************************************************
 *
 * Block device functions
 *
 ********************************************************************/

/*
 * block device open routine
 */
static int lvm_blk_open(struct inode *inode, struct file *file)
{
Linus Torvalds's avatar
Linus Torvalds committed
793
	int minor = minor(inode->i_rdev);
Linus Torvalds's avatar
Linus Torvalds committed
794 795 796
	lv_t *lv_ptr;
	vg_t *vg_ptr = vg[VG_BLK(minor)];

Linus Torvalds's avatar
Linus Torvalds committed
797 798
	P_DEV("blk_open MINOR: %d  VG#: %d  LV#: %d  mode: %s%s\n",
	      minor, VG_BLK(minor), LV_BLK(minor), MODE_TO_STR(file->f_mode));
Linus Torvalds's avatar
Linus Torvalds committed
799 800 801 802 803 804 805 806 807 808 809 810 811

#ifdef LVM_TOTAL_RESET
	if (lvm_reset_spindown > 0)
		return -EPERM;
#endif

	if (vg_ptr != NULL &&
	    (vg_ptr->vg_status & VG_ACTIVE) &&
	    (lv_ptr = vg_ptr->lv[LV_BLK(minor)]) != NULL &&
	    LV_BLK(minor) >= 0 &&
	    LV_BLK(minor) < vg_ptr->lv_max) {

		/* Check parallel LV spindown (LV remove) */
Dave Jones's avatar
Dave Jones committed
812
		if (lv_ptr->u.lv_status & LV_SPINDOWN) return -EPERM;
Linus Torvalds's avatar
Linus Torvalds committed
813 814

		/* Check inactive LV and open for read/write */
Linus Torvalds's avatar
Linus Torvalds committed
815 816 817
		/* We need to be able to "read" an inactive LV
		   to re-activate it again */
		if ((file->f_mode & FMODE_WRITE) &&
Dave Jones's avatar
Dave Jones committed
818
		    (!(lv_ptr->u.lv_status & LV_ACTIVE)))
Linus Torvalds's avatar
Linus Torvalds committed
819 820
		    return -EPERM;

Dave Jones's avatar
Dave Jones committed
821
		if (!(lv_ptr->u.lv_access & LV_WRITE) &&
Linus Torvalds's avatar
Linus Torvalds committed
822 823
		    (file->f_mode & FMODE_WRITE))
			return -EACCES;
Linus Torvalds's avatar
Linus Torvalds committed
824 825 826


                /* be sure to increment VG counter */
Dave Jones's avatar
Dave Jones committed
827 828
		if (lv_ptr->u.lv_open == 0) vg_ptr->lv_open++;
		lv_ptr->u.lv_open++;
Linus Torvalds's avatar
Linus Torvalds committed
829

Linus Torvalds's avatar
Linus Torvalds committed
830 831
		MOD_INC_USE_COUNT;

Dave Jones's avatar
Dave Jones committed
832
		P_DEV("blk_open OK, LV size %d\n", lv_ptr->u.lv_size);
Linus Torvalds's avatar
Linus Torvalds committed
833 834 835 836 837 838 839 840 841 842 843 844 845

		return 0;
	}
	return -ENXIO;
} /* lvm_blk_open() */


/*
 * block device i/o-control routine
 */
static int lvm_blk_ioctl(struct inode *inode, struct file *file,
			 uint command, ulong a)
{
Linus Torvalds's avatar
Linus Torvalds committed
846
	int minor = minor(inode->i_rdev);
Linus Torvalds's avatar
Linus Torvalds committed
847 848 849 850 851
	vg_t *vg_ptr = vg[VG_BLK(minor)];
	lv_t *lv_ptr = vg_ptr->lv[LV_BLK(minor)];
	void *arg = (void *) a;
	struct hd_geometry *hd = (struct hd_geometry *) a;

Linus Torvalds's avatar
Linus Torvalds committed
852 853 854
	P_IOCTL("blk MINOR: %d  command: 0x%X  arg: %p  VG#: %d  LV#: %d  "
		"mode: %s%s\n", minor, command, arg, VG_BLK(minor),
		LV_BLK(minor), MODE_TO_STR(file->f_mode));
Linus Torvalds's avatar
Linus Torvalds committed
855 856

	switch (command) {
Linus Torvalds's avatar
Linus Torvalds committed
857 858
	case BLKSSZGET:
		/* get block device sector size as needed e.g. by fdisk */
859
		return put_user(bdev_hardsect_size(inode->i_bdev), (int *) arg);
Linus Torvalds's avatar
Linus Torvalds committed
860

Linus Torvalds's avatar
Linus Torvalds committed
861 862
	case BLKGETSIZE:
		/* return device size */
Dave Jones's avatar
Dave Jones committed
863 864
		P_IOCTL("BLKGETSIZE: %u\n", lv_ptr->u.lv_size);
		if (put_user(lv_ptr->u.lv_size, (unsigned long *)arg))
Linus Torvalds's avatar
Linus Torvalds committed
865
			return -EFAULT;
Linus Torvalds's avatar
Linus Torvalds committed
866 867
		break;

Linus Torvalds's avatar
Linus Torvalds committed
868
	case BLKGETSIZE64:
Dave Jones's avatar
Dave Jones committed
869
		if (put_user((u64)lv_ptr->u.lv_size << 9, (u64 *)arg))
Linus Torvalds's avatar
Linus Torvalds committed
870 871 872
			return -EFAULT;
		break;

Linus Torvalds's avatar
Linus Torvalds committed
873 874
	case HDIO_GETGEO:
		/* get disk geometry */
Linus Torvalds's avatar
Linus Torvalds committed
875
		P_IOCTL("%s -- lvm_blk_ioctl -- HDIO_GETGEO\n", lvm_name);
Linus Torvalds's avatar
Linus Torvalds committed
876 877 878 879 880 881
		if (hd == NULL)
			return -EINVAL;
		{
			unsigned char heads = 64;
			unsigned char sectors = 32;
			long start = 0;
Dave Jones's avatar
Dave Jones committed
882
			short cylinders = lv_ptr->u.lv_size / heads / sectors;
Linus Torvalds's avatar
Linus Torvalds committed
883 884 885 886 887 888 889 890 891 892 893

			if (copy_to_user((char *) &hd->heads, &heads,
					 sizeof(heads)) != 0 ||
			    copy_to_user((char *) &hd->sectors, &sectors,
					 sizeof(sectors)) != 0 ||
			    copy_to_user((short *) &hd->cylinders,
				   &cylinders, sizeof(cylinders)) != 0 ||
			    copy_to_user((long *) &hd->start, &start,
					 sizeof(start)) != 0)
				return -EFAULT;

Linus Torvalds's avatar
Linus Torvalds committed
894 895 896
			P_IOCTL("%s -- lvm_blk_ioctl -- cylinders: %d\n",
				lvm_name, cylinders);
		}
Linus Torvalds's avatar
Linus Torvalds committed
897 898 899 900 901 902
		break;


	case LV_SET_ACCESS:
		/* set access flags of a logical volume */
		if (!capable(CAP_SYS_ADMIN)) return -EACCES;
Dave Jones's avatar
Dave Jones committed
903 904 905
		lv_ptr->u.lv_access = (ulong) arg;
		if ( lv_ptr->u.lv_access & LV_WRITE)
			set_device_ro(lv_ptr->u.lv_dev, 0);
Linus Torvalds's avatar
Linus Torvalds committed
906
		else
Dave Jones's avatar
Dave Jones committed
907
			set_device_ro(lv_ptr->u.lv_dev, 1);
Linus Torvalds's avatar
Linus Torvalds committed
908 909 910 911 912 913
		break;


	case LV_SET_STATUS:
		/* set status flags of a logical volume */
		if (!capable(CAP_SYS_ADMIN)) return -EACCES;
Dave Jones's avatar
Dave Jones committed
914
		if (!((ulong) arg & LV_ACTIVE) && lv_ptr->u.lv_open > 1)
Linus Torvalds's avatar
Linus Torvalds committed
915
			return -EPERM;
Dave Jones's avatar
Dave Jones committed
916
		lv_ptr->u.lv_status = (ulong) arg;
Linus Torvalds's avatar
Linus Torvalds committed
917 918 919
		break;

	case LV_BMAP:
Linus Torvalds's avatar
Linus Torvalds committed
920 921
                /* turn logical block into (dev_t, block).  non privileged. */
                /* don't bmap a snapshot, since the mapping can change */
Dave Jones's avatar
Dave Jones committed
922
		if(lv_ptr->u.lv_access & LV_SNAPSHOT)
Linus Torvalds's avatar
Linus Torvalds committed
923 924
			return -EPERM;

Linus Torvalds's avatar
Linus Torvalds committed
925 926 927 928 929
		return lvm_user_bmap(inode, (struct lv_bmap *) arg);

	case LV_SET_ALLOCATION:
		/* set allocation flags of a logical volume */
		if (!capable(CAP_SYS_ADMIN)) return -EACCES;
Dave Jones's avatar
Dave Jones committed
930
		lv_ptr->u.lv_allocation = (ulong) arg;
Linus Torvalds's avatar
Linus Torvalds committed
931 932 933
		break;

	case LV_SNAPSHOT_USE_RATE:
Linus Torvalds's avatar
Linus Torvalds committed
934
		return lvm_get_snapshot_use_rate(lv_ptr, arg);
Linus Torvalds's avatar
Linus Torvalds committed
935 936 937

	default:
		printk(KERN_WARNING
Linus Torvalds's avatar
Linus Torvalds committed
938
		       "%s -- lvm_blk_ioctl: unknown command 0x%x\n",
Linus Torvalds's avatar
Linus Torvalds committed
939 940 941 942 943 944 945 946 947 948 949 950 951
		       lvm_name, command);
		return -EINVAL;
	}

	return 0;
} /* lvm_blk_ioctl() */


/*
 * block device close routine
 */
static int lvm_blk_close(struct inode *inode, struct file *file)
{
Linus Torvalds's avatar
Linus Torvalds committed
952
	int minor = minor(inode->i_rdev);
Linus Torvalds's avatar
Linus Torvalds committed
953 954 955
	vg_t *vg_ptr = vg[VG_BLK(minor)];
	lv_t *lv_ptr = vg_ptr->lv[LV_BLK(minor)];

Linus Torvalds's avatar
Linus Torvalds committed
956 957
	P_DEV("blk_close MINOR: %d  VG#: %d  LV#: %d\n",
	      minor, VG_BLK(minor), LV_BLK(minor));
Linus Torvalds's avatar
Linus Torvalds committed
958

Dave Jones's avatar
Dave Jones committed
959 960
	if (lv_ptr->u.lv_open == 1) vg_ptr->lv_open--;
	lv_ptr->u.lv_open--;
Linus Torvalds's avatar
Linus Torvalds committed
961

Linus Torvalds's avatar
Linus Torvalds committed
962 963
	MOD_DEC_USE_COUNT;

Linus Torvalds's avatar
Linus Torvalds committed
964 965 966
	return 0;
} /* lvm_blk_close() */

Linus Torvalds's avatar
Linus Torvalds committed
967 968 969 970
static int lvm_get_snapshot_use_rate(lv_t *lv, void *arg)
{
	lv_snapshot_use_rate_req_t lv_rate_req;

Dave Jones's avatar
Dave Jones committed
971
	if (!(lv->u.lv_access & LV_SNAPSHOT))
Linus Torvalds's avatar
Linus Torvalds committed
972 973 974 975 976 977 978 979 980 981 982
		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;
Dave Jones's avatar
Dave Jones committed
983
		if (lv->u.lv_remap_ptr * 100 / lv->u.lv_remap_end <
Linus Torvalds's avatar
Linus Torvalds committed
984 985 986 987 988 989 990 991 992 993
		    lv->lv_snapshot_use_rate)
			interruptible_sleep_on(&lv->lv_snapshot_wait);
		break;

	case O_NONBLOCK:
		break;

	default:
		return -EINVAL;
	}
Dave Jones's avatar
Dave Jones committed
994
	lv_rate_req.rate = lv->u.lv_remap_ptr * 100 / lv->u.lv_remap_end;
Linus Torvalds's avatar
Linus Torvalds committed
995 996 997 998

	return copy_to_user(arg, &lv_rate_req,
			    sizeof(lv_rate_req)) ? -EFAULT : 0;
}
Linus Torvalds's avatar
Linus Torvalds committed
999 1000 1001

static int lvm_user_bmap(struct inode *inode, struct lv_bmap *user_result)
{
Linus Torvalds's avatar
Linus Torvalds committed
1002
	struct bio bio;
Linus Torvalds's avatar
Linus Torvalds committed
1003 1004
	unsigned long block;
	int err;
Linus Torvalds's avatar
Linus Torvalds committed
1005

Linus Torvalds's avatar
Linus Torvalds committed
1006
	if (get_user(block, &user_result->lv_block))
Linus Torvalds's avatar
Linus Torvalds committed
1007 1008
		return -EFAULT;

Linus Torvalds's avatar
Linus Torvalds committed
1009 1010
	memset(&bio,0,sizeof(bio));
	bio.bi_dev = inode->i_rdev;
Dave Jones's avatar
Dave Jones committed
1011
	bio.bi_size = block_size(bio.bi_dev); /* NEEDED by bio_sectors */
Linus Torvalds's avatar
Linus Torvalds committed
1012 1013 1014
 	bio.bi_sector = block * bio_sectors(&bio);
	bio.bi_rw = READ;
	if ((err=lvm_map(&bio)) < 0)  {
Linus Torvalds's avatar
Linus Torvalds committed
1015 1016
		printk("lvm map failed: %d\n", err);
		return -EINVAL;
Linus Torvalds's avatar
Linus Torvalds committed
1017
	}
Linus Torvalds's avatar
Linus Torvalds committed
1018

Linus Torvalds's avatar
Linus Torvalds committed
1019 1020
	return put_user(kdev_t_to_nr(bio.bi_dev), &user_result->lv_dev) ||
	       put_user(bio.bi_sector/bio_sectors(&bio), &user_result->lv_block) ?
Linus Torvalds's avatar
Linus Torvalds committed
1021
	       -EFAULT : 0;
Linus Torvalds's avatar
Linus Torvalds committed
1022
}
Linus Torvalds's avatar
Linus Torvalds committed
1023 1024 1025


/*
Linus Torvalds's avatar
Linus Torvalds committed
1026 1027
 * block device support function for /usr/src/linux/drivers/block/ll_rw_blk.c
 * (see init_module/lvm_init)
Linus Torvalds's avatar
Linus Torvalds committed
1028
 */
Linus Torvalds's avatar
Linus Torvalds committed
1029 1030
static void __remap_snapshot(kdev_t rdev, ulong rsector,
			     ulong pe_start, lv_t *lv, vg_t *vg) {
Linus Torvalds's avatar
Linus Torvalds committed
1031

Linus Torvalds's avatar
Linus Torvalds committed
1032 1033
	/* copy a chunk from the origin to a snapshot device */
	down_write(&lv->lv_lock);
Linus Torvalds's avatar
Linus Torvalds committed
1034

Linus Torvalds's avatar
Linus Torvalds committed
1035 1036 1037 1038 1039
	/* we must redo lvm_snapshot_remap_block in order to avoid a
	   race condition in the gap where no lock was held */
	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);
Linus Torvalds's avatar
Linus Torvalds committed
1040

Linus Torvalds's avatar
Linus Torvalds committed
1041
	up_write(&lv->lv_lock);
Linus Torvalds's avatar
Linus Torvalds committed
1042 1043
}

Linus Torvalds's avatar
Linus Torvalds committed
1044 1045 1046
static inline void _remap_snapshot(kdev_t rdev, ulong rsector,
				   ulong pe_start, lv_t *lv, vg_t *vg) {
	int r;
Linus Torvalds's avatar
Linus Torvalds committed
1047

Linus Torvalds's avatar
Linus Torvalds committed
1048 1049 1050 1051 1052 1053 1054 1055
	/* check to see if this chunk is already in the snapshot */
	down_read(&lv->lv_lock);
	r = lvm_snapshot_remap_block(&rdev, &rsector, pe_start, lv);
	up_read(&lv->lv_lock);

	if (!r)
		/* we haven't yet copied this block to the snapshot */
		__remap_snapshot(rdev, rsector, pe_start, lv, vg);
Linus Torvalds's avatar
Linus Torvalds committed
1056 1057 1058 1059
}


/*
Linus Torvalds's avatar
Linus Torvalds committed
1060
 * extents destined for a pe that is on the move should be deferred
Linus Torvalds's avatar
Linus Torvalds committed
1061
 */
Linus Torvalds's avatar
Linus Torvalds committed
1062 1063
static inline int _should_defer(kdev_t pv, ulong sector, uint32_t pe_size) {
	return ((pe_lock_req.lock == LOCK_PE) &&
Linus Torvalds's avatar
Linus Torvalds committed
1064
		kdev_same(pv, pe_lock_req.data.pv_dev) &&
Linus Torvalds's avatar
Linus Torvalds committed
1065 1066 1067
		(sector >= pe_lock_req.data.pv_offset) &&
		(sector < (pe_lock_req.data.pv_offset + pe_size)));
}
Linus Torvalds's avatar
Linus Torvalds committed
1068

Linus Torvalds's avatar
Linus Torvalds committed
1069
static inline int _defer_extent(struct bio *bh, int rw,
Linus Torvalds's avatar
Linus Torvalds committed
1070
				kdev_t pv, ulong sector, uint32_t pe_size)
Linus Torvalds's avatar
Linus Torvalds committed
1071
{
Linus Torvalds's avatar
Linus Torvalds committed
1072 1073 1074 1075 1076 1077 1078 1079 1080
	if (pe_lock_req.lock == LOCK_PE) {
		down_read(&_pe_lock);
		if (_should_defer(pv, sector, pe_size)) {
			up_read(&_pe_lock);
			down_write(&_pe_lock);
			if (_should_defer(pv, sector, pe_size))
				_queue_io(bh, rw);
			up_write(&_pe_lock);
			return 1;
Linus Torvalds's avatar
Linus Torvalds committed
1081
		}
Linus Torvalds's avatar
Linus Torvalds committed
1082
		up_read(&_pe_lock);
Linus Torvalds's avatar
Linus Torvalds committed
1083
	}
Linus Torvalds's avatar
Linus Torvalds committed
1084
	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
1085 1086
}

Linus Torvalds's avatar
Linus Torvalds committed
1087
static int lvm_map(struct bio *bi)
Linus Torvalds's avatar
Linus Torvalds committed
1088
{
Linus Torvalds's avatar
Linus Torvalds committed
1089
	int minor = minor(bi->bi_dev);
Linus Torvalds's avatar
Linus Torvalds committed
1090 1091
	ulong index;
	ulong pe_start;
Linus Torvalds's avatar
Linus Torvalds committed
1092 1093
	ulong size = bio_sectors(bi);
	ulong rsector_org = bi->bi_sector;
Linus Torvalds's avatar
Linus Torvalds committed
1094 1095
	ulong rsector_map;
	kdev_t rdev_map;
Linus Torvalds's avatar
Linus Torvalds committed
1096 1097
	vg_t *vg_this = vg[VG_BLK(minor)];
	lv_t *lv = vg_this->lv[LV_BLK(minor)];
Linus Torvalds's avatar
Linus Torvalds committed
1098
	int rw = bio_rw(bi);
Linus Torvalds's avatar
Linus Torvalds committed
1099

Linus Torvalds's avatar
Linus Torvalds committed
1100
	down_read(&lv->lv_lock);
Dave Jones's avatar
Dave Jones committed
1101
	if (!(lv->u.lv_status & LV_ACTIVE)) {
Linus Torvalds's avatar
Linus Torvalds committed
1102 1103
		printk(KERN_ALERT
		       "%s - lvm_map: ll_rw_blk for inactive LV %s\n",
Dave Jones's avatar
Dave Jones committed
1104
		       lvm_name, lv->u.lv_name);
Linus Torvalds's avatar
Linus Torvalds committed
1105
		goto bad;
Linus Torvalds's avatar
Linus Torvalds committed
1106 1107 1108
	}

	if ((rw == WRITE || rw == WRITEA) &&
Dave Jones's avatar
Dave Jones committed
1109
	    !(lv->u.lv_access & LV_WRITE)) {
Linus Torvalds's avatar
Linus Torvalds committed
1110
		printk(KERN_CRIT
Linus Torvalds's avatar
Linus Torvalds committed
1111
		       "%s - lvm_map: ll_rw_blk write for readonly LV %s\n",
Dave Jones's avatar
Dave Jones committed
1112
		       lvm_name, lv->u.lv_name);
Linus Torvalds's avatar
Linus Torvalds committed
1113
		goto bad;
Linus Torvalds's avatar
Linus Torvalds committed
1114
	}
Linus Torvalds's avatar
Linus Torvalds committed
1115

Linus Torvalds's avatar
Linus Torvalds committed
1116
	P_MAP("%s - lvm_map minor: %d  *rdev: %s  *rsector: %lu  size:%lu\n",
Linus Torvalds's avatar
Linus Torvalds committed
1117
	      lvm_name, minor,
Linus Torvalds's avatar
Linus Torvalds committed
1118
	      kdevname(bi->bi_dev),
Linus Torvalds's avatar
Linus Torvalds committed
1119
	      rsector_org, size);
Linus Torvalds's avatar
Linus Torvalds committed
1120

Dave Jones's avatar
Dave Jones committed
1121
	if (rsector_org + size > lv->u.lv_size) {
Linus Torvalds's avatar
Linus Torvalds committed
1122 1123 1124
		printk(KERN_ALERT
		       "%s - lvm_map access beyond end of device; *rsector: "
                       "%lu or size: %lu wrong for minor: %2d\n",
Linus Torvalds's avatar
Linus Torvalds committed
1125 1126
                       lvm_name, rsector_org, size, minor);
		goto bad;
Linus Torvalds's avatar
Linus Torvalds committed
1127 1128
	}

Linus Torvalds's avatar
Linus Torvalds committed
1129

Dave Jones's avatar
Dave Jones committed
1130
	if (lv->u.lv_stripes < 2) { /* linear mapping */
Linus Torvalds's avatar
Linus Torvalds committed
1131
		/* get the index */
Linus Torvalds's avatar
Linus Torvalds committed
1132
		index = rsector_org / vg_this->pe_size;
Dave Jones's avatar
Dave Jones committed
1133 1134
		pe_start = lv->u.lv_current_pe[index].pe;
		rsector_map = lv->u.lv_current_pe[index].pe +
Linus Torvalds's avatar
Linus Torvalds committed
1135
			(rsector_org % vg_this->pe_size);
Dave Jones's avatar
Dave Jones committed
1136
		rdev_map = lv->u.lv_current_pe[index].dev;
Linus Torvalds's avatar
Linus Torvalds committed
1137

Dave Jones's avatar
Dave Jones committed
1138 1139
		P_MAP("u.lv_current_pe[%ld].pe: %d  rdev: %s  rsector:%ld\n",
		      index, lv->u.lv_current_pe[index].pe,
Linus Torvalds's avatar
Linus Torvalds committed
1140 1141 1142
		      kdevname(rdev_map), rsector_map);

	} else {		/* striped mapping */
Linus Torvalds's avatar
Linus Torvalds committed
1143 1144 1145
		ulong stripe_index;
		ulong stripe_length;

Dave Jones's avatar
Dave Jones committed
1146
		stripe_length = vg_this->pe_size * lv->u.lv_stripes;
Linus Torvalds's avatar
Linus Torvalds committed
1147
		stripe_index = (rsector_org % stripe_length) /
Dave Jones's avatar
Dave Jones committed
1148
			lv->u.lv_stripesize;
Linus Torvalds's avatar
Linus Torvalds committed
1149
		index = rsector_org / stripe_length +
Dave Jones's avatar
Dave Jones committed
1150 1151 1152 1153
			(stripe_index % lv->u.lv_stripes) *
			(lv->u.lv_allocated_le / lv->u.lv_stripes);
		pe_start = lv->u.lv_current_pe[index].pe;
		rsector_map = lv->u.lv_current_pe[index].pe +
Linus Torvalds's avatar
Linus Torvalds committed
1154
			(rsector_org % stripe_length) -
Dave Jones's avatar
Dave Jones committed
1155 1156 1157 1158
			(stripe_index % lv->u.lv_stripes) * lv->u.lv_stripesize -
			stripe_index / lv->u.lv_stripes *
			(lv->u.lv_stripes - 1) * lv->u.lv_stripesize;
		rdev_map = lv->u.lv_current_pe[index].dev;
Linus Torvalds's avatar
Linus Torvalds committed
1159

Dave Jones's avatar
Dave Jones committed
1160
		P_MAP("u.lv_current_pe[%ld].pe: %d  rdev: %s  rsector:%ld\n"
Linus Torvalds's avatar
Linus Torvalds committed
1161
		      "stripe_length: %ld  stripe_index: %ld\n",
Dave Jones's avatar
Dave Jones committed
1162
		      index, lv->u.lv_current_pe[index].pe, kdevname(rdev_map),
Linus Torvalds's avatar
Linus Torvalds committed
1163 1164 1165 1166 1167 1168 1169 1170 1171
		      rsector_map, stripe_length, stripe_index);
	}

	/*
	 * Queue writes to physical extents on the move until move completes.
	 * Don't get _pe_lock until there is a reasonable expectation that
	 * we need to queue this request, because this is in the fast path.
	 */
	if (rw == WRITE || rw == WRITEA) {
Linus Torvalds's avatar
Linus Torvalds committed
1172
		if(_defer_extent(bi, rw, rdev_map,
Linus Torvalds's avatar
Linus Torvalds committed
1173 1174 1175 1176
				 rsector_map, vg_this->pe_size)) {

			up_read(&lv->lv_lock);
			return 0;
Linus Torvalds's avatar
Linus Torvalds committed
1177
		}
Linus Torvalds's avatar
Linus Torvalds committed
1178

Dave Jones's avatar
Dave Jones committed
1179
		lv->u.lv_current_pe[index].writes++;	/* statistic */
Linus Torvalds's avatar
Linus Torvalds committed
1180
	} else
Dave Jones's avatar
Dave Jones committed
1181
		lv->u.lv_current_pe[index].reads++;	/* statistic */
Linus Torvalds's avatar
Linus Torvalds committed
1182 1183

	/* snapshot volume exception handling on physical device address base */
Dave Jones's avatar
Dave Jones committed
1184
	if (!(lv->u.lv_access & (LV_SNAPSHOT|LV_SNAPSHOT_ORG)))
Linus Torvalds's avatar
Linus Torvalds committed
1185 1186
		goto out;

Dave Jones's avatar
Dave Jones committed
1187 1188
	if (lv->u.lv_access & LV_SNAPSHOT) { /* remap snapshot */
		if (lv->u.lv_block_exception)
Linus Torvalds's avatar
Linus Torvalds committed
1189 1190 1191 1192 1193 1194 1195 1196 1197 1198
			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 */
Dave Jones's avatar
Dave Jones committed
1199 1200
		for (snap = lv->u.lv_snapshot_next; snap;
		     snap = snap->u.lv_snapshot_next) {
Linus Torvalds's avatar
Linus Torvalds committed
1201
			/* Check for inactive snapshot */
Dave Jones's avatar
Dave Jones committed
1202
			if (!(snap->u.lv_status & LV_ACTIVE))
Linus Torvalds's avatar
Linus Torvalds committed
1203 1204 1205 1206 1207 1208
				continue;

			/* Serializes the COW with the accesses to the
			   snapshot device */
			_remap_snapshot(rdev_map, rsector_map,
					 pe_start, snap, vg_this);
Linus Torvalds's avatar
Linus Torvalds committed
1209
		}
Linus Torvalds's avatar
Linus Torvalds committed
1210
 	}
Linus Torvalds's avatar
Linus Torvalds committed
1211

Linus Torvalds's avatar
Linus Torvalds committed
1212
 out:
Linus Torvalds's avatar
Linus Torvalds committed
1213 1214
	bi->bi_dev = rdev_map;
	bi->bi_sector = rsector_map;
Linus Torvalds's avatar
Linus Torvalds committed
1215 1216 1217 1218
	up_read(&lv->lv_lock);
	return 1;

 bad:
Linus Torvalds's avatar
Linus Torvalds committed
1219
	bio_io_error(bi);
Linus Torvalds's avatar
Linus Torvalds committed
1220 1221
	up_read(&lv->lv_lock);
	return -1;
Linus Torvalds's avatar
Linus Torvalds committed
1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240
} /* lvm_map() */


/*
 * internal support functions
 */

#ifdef LVM_HD_NAME
/*
 * generate "hard disk" name
 */
void lvm_hd_name(char *buf, int minor)
{
	int len = 0;
	lv_t *lv_ptr;

	if (vg[VG_BLK(minor)] == NULL ||
	    (lv_ptr = vg[VG_BLK(minor)]->lv[LV_BLK(minor)]) == NULL)
		return;
Dave Jones's avatar
Dave Jones committed
1241 1242
	len = strlen(lv_ptr->u.lv_name) - 5;
	memcpy(buf, &lv_ptr->u.lv_name[5], len);
Linus Torvalds's avatar
Linus Torvalds committed
1243 1244 1245 1246 1247 1248 1249 1250 1251
	buf[len] = 0;
	return;
}
#endif


/*
 * make request function
 */
Linus Torvalds's avatar
Linus Torvalds committed
1252 1253 1254
static int lvm_make_request_fn(request_queue_t *q, struct bio *bio)
{
	return (lvm_map(bio) <= 0) ? 0 : 1;
Linus Torvalds's avatar
Linus Torvalds committed
1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270
}


/********************************************************************
 *
 * Character device support functions
 *
 ********************************************************************/
/*
 * character device support function logical volume manager lock
 */
static int lvm_do_lock_lvm(void)
{
lock_try_again:
	spin_lock(&lvm_lock);
	if (lock != 0 && lock != current->pid) {
Linus Torvalds's avatar
Linus Torvalds committed
1271
		P_DEV("lvm_do_lock_lvm: locked by pid %d ...\n", lock);
Linus Torvalds's avatar
Linus Torvalds committed
1272 1273
		spin_unlock(&lvm_lock);
		interruptible_sleep_on(&lvm_wait);
Dave Jones's avatar
Dave Jones committed
1274
		if (signal_pending(current))
Linus Torvalds's avatar
Linus Torvalds committed
1275 1276 1277 1278 1279 1280 1281 1282
			return -EINTR;
#ifdef LVM_TOTAL_RESET
		if (lvm_reset_spindown > 0)
			return -EACCES;
#endif
		goto lock_try_again;
	}
	lock = current->pid;
Linus Torvalds's avatar
Linus Torvalds committed
1283
	P_DEV("lvm_do_lock_lvm: locking LVM for pid %d\n", lock);
Linus Torvalds's avatar
Linus Torvalds committed
1284 1285 1286 1287 1288 1289 1290 1291 1292 1293
	spin_unlock(&lvm_lock);
	return 0;
} /* lvm_do_lock_lvm */


/*
 * character device support function lock/unlock physical extend
 */
static int lvm_do_pe_lock_unlock(vg_t *vg_ptr, void *arg)
{
Linus Torvalds's avatar
Linus Torvalds committed
1294
	pe_lock_req_t new_lock;
Linus Torvalds's avatar
Linus Torvalds committed
1295
	struct bio *bh;
Linus Torvalds's avatar
Linus Torvalds committed
1296 1297 1298
	uint p;

	if (vg_ptr == NULL) return -ENXIO;
Linus Torvalds's avatar
Linus Torvalds committed
1299 1300
	if (copy_from_user(&new_lock, arg, sizeof(new_lock)) != 0)
		return -EFAULT;
Linus Torvalds's avatar
Linus Torvalds committed
1301

Linus Torvalds's avatar
Linus Torvalds committed
1302
	switch (new_lock.lock) {
Linus Torvalds's avatar
Linus Torvalds committed
1303 1304 1305
	case LOCK_PE:
		for (p = 0; p < vg_ptr->pv_max; p++) {
			if (vg_ptr->pv[p] != NULL &&
Linus Torvalds's avatar
Linus Torvalds committed
1306 1307
			    kdev_same(new_lock.data.pv_dev,
				      vg_ptr->pv[p]->pv_dev))
Linus Torvalds's avatar
Linus Torvalds committed
1308 1309 1310 1311
				break;
		}
		if (p == vg_ptr->pv_max) return -ENXIO;

Linus Torvalds's avatar
Linus Torvalds committed
1312 1313 1314 1315 1316 1317 1318
		/*
		 * this sync releaves memory pressure to lessen the
		 * likelyhood of pvmove being paged out - resulting in
		 * deadlock.
		 *
		 * This method of doing a pvmove is broken
		 */
Linus Torvalds's avatar
Linus Torvalds committed
1319
		fsync_dev(pe_lock_req.data.lv_dev);
Linus Torvalds's avatar
Linus Torvalds committed
1320 1321 1322 1323 1324 1325 1326

		down_write(&_pe_lock);
		if (pe_lock_req.lock == LOCK_PE) {
			up_write(&_pe_lock);
			return -EBUSY;
		}

Dave Jones's avatar
Dave Jones committed
1327
		/* Should we do to_kdev_t() on the pv_dev and u.lv_dev??? */
Linus Torvalds's avatar
Linus Torvalds committed
1328
		pe_lock_req.lock = LOCK_PE;
Linus Torvalds's avatar
Linus Torvalds committed
1329 1330 1331 1332 1333 1334 1335
		pe_lock_req.data.lv_dev = new_lock.data.lv_dev;
		pe_lock_req.data.pv_dev = new_lock.data.pv_dev;
		pe_lock_req.data.pv_offset = new_lock.data.pv_offset;
		up_write(&_pe_lock);

		/* some requests may have got through since the fsync */
		fsync_dev(pe_lock_req.data.pv_dev);
Linus Torvalds's avatar
Linus Torvalds committed
1336 1337 1338
		break;

	case UNLOCK_PE:
Linus Torvalds's avatar
Linus Torvalds committed
1339
		down_write(&_pe_lock);
Linus Torvalds's avatar
Linus Torvalds committed
1340
		pe_lock_req.lock = UNLOCK_PE;
Linus Torvalds's avatar
Linus Torvalds committed
1341 1342
		pe_lock_req.data.lv_dev = NODEV;
		pe_lock_req.data.pv_dev = NODEV;
Linus Torvalds's avatar
Linus Torvalds committed
1343
		pe_lock_req.data.pv_offset = 0;
Linus Torvalds's avatar
Linus Torvalds committed
1344 1345 1346 1347 1348
		bh = _dequeue_io();
		up_write(&_pe_lock);

		/* handle all deferred io for this PE */
		_flush_io(bh);
Linus Torvalds's avatar
Linus Torvalds committed
1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373
		break;

	default:
		return -EINVAL;
	}
	return 0;
}


/*
 * character device support function logical extend remap
 */
static int lvm_do_le_remap(vg_t *vg_ptr, void *arg)
{
	uint l, le;
	lv_t *lv_ptr;

	if (vg_ptr == NULL) return -ENXIO;
	if (copy_from_user(&le_remap_req, arg,
			   sizeof(le_remap_req_t)) != 0)
		return -EFAULT;

	for (l = 0; l < vg_ptr->lv_max; l++) {
		lv_ptr = vg_ptr->lv[l];
		if (lv_ptr != NULL &&
Dave Jones's avatar
Dave Jones committed
1374
		    strcmp(lv_ptr->u.lv_name,
Linus Torvalds's avatar
Linus Torvalds committed
1375
			       le_remap_req.lv_name) == 0) {
Dave Jones's avatar
Dave Jones committed
1376 1377
			for (le = 0; le < lv_ptr->u.lv_allocated_le; le++) {
			  if (kdev_same(lv_ptr->u.lv_current_pe[le].dev,
Linus Torvalds's avatar
Linus Torvalds committed
1378
					le_remap_req.old_dev) &&
Dave Jones's avatar
Dave Jones committed
1379
				    lv_ptr->u.lv_current_pe[le].pe ==
Linus Torvalds's avatar
Linus Torvalds committed
1380
				    le_remap_req.old_pe) {
Dave Jones's avatar
Dave Jones committed
1381
					lv_ptr->u.lv_current_pe[le].dev =
Linus Torvalds's avatar
Linus Torvalds committed
1382
					    le_remap_req.new_dev;
Dave Jones's avatar
Dave Jones committed
1383
					lv_ptr->u.lv_current_pe[le].pe =
Linus Torvalds's avatar
Linus Torvalds committed
1384
					    le_remap_req.new_pe;
Linus Torvalds's avatar
Linus Torvalds committed
1385 1386

					__update_hardsectsize(lv_ptr);
Linus Torvalds's avatar
Linus Torvalds committed
1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399
					return 0;
				}
			}
			return -EINVAL;
		}
	}
	return -ENXIO;
} /* lvm_do_le_remap() */


/*
 * character device support function VGDA create
 */
Linus Torvalds's avatar
Linus Torvalds committed
1400
static int lvm_do_vg_create(void *arg, int minor)
Linus Torvalds's avatar
Linus Torvalds committed
1401 1402 1403 1404 1405
{
	int ret = 0;
	ulong l, ls = 0, p, size;
	vg_t *vg_ptr;
	lv_t **snap_lv_ptr;
Linus Torvalds's avatar
Linus Torvalds committed
1406
	lv_t *tmplv;
Linus Torvalds's avatar
Linus Torvalds committed
1407 1408 1409 1410 1411 1412 1413

	if ((vg_ptr = kmalloc(sizeof(vg_t),GFP_KERNEL)) == NULL) {
		printk(KERN_CRIT
		       "%s -- VG_CREATE: kmalloc error VG at line %d\n",
		       lvm_name, __LINE__);
		return -ENOMEM;
	}
Linus Torvalds's avatar
Linus Torvalds committed
1414

Linus Torvalds's avatar
Linus Torvalds committed
1415 1416
	/* get the volume group structure */
	if (copy_from_user(vg_ptr, arg, sizeof(vg_t)) != 0) {
Linus Torvalds's avatar
Linus Torvalds committed
1417 1418
		P_IOCTL("lvm_do_vg_create ERROR: copy VG ptr %p (%d bytes)\n",
			arg, sizeof(vg_t));
Linus Torvalds's avatar
Linus Torvalds committed
1419 1420 1421 1422
		kfree(vg_ptr);
		return -EFAULT;
	}

Linus Torvalds's avatar
Linus Torvalds committed
1423 1424


Linus Torvalds's avatar
Linus Torvalds committed
1425 1426 1427 1428 1429 1430 1431 1432 1433 1434
        /* 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) {
		P_IOCTL("lvm_do_vg_create ERROR: VG %d in use\n", minor);
		kfree(vg_ptr);
  	     	return -EPERM;
	}

Linus Torvalds's avatar
Linus Torvalds committed
1435 1436
	/* we are not that active so far... */
	vg_ptr->vg_status &= ~VG_ACTIVE;
Linus Torvalds's avatar
Linus Torvalds committed
1437
	vg_ptr->pe_allocated = 0;
Linus Torvalds's avatar
Linus Torvalds committed
1438 1439 1440 1441 1442 1443 1444 1445

	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);
		return -EPERM;
	}
Linus Torvalds's avatar
Linus Torvalds committed
1446

Linus Torvalds's avatar
Linus Torvalds committed
1447 1448 1449 1450 1451 1452 1453 1454
	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);
		return -EPERM;
	}

Linus Torvalds's avatar
Linus Torvalds committed
1455 1456 1457 1458 1459
	/* create devfs and procfs entries */
	lvm_fs_create_vg(vg_ptr);

	vg[VG_CHR(minor)] = vg_ptr;

Linus Torvalds's avatar
Linus Torvalds committed
1460 1461 1462
	/* get the physical volume structures */
	vg_ptr->pv_act = vg_ptr->pv_cur = 0;
	for (p = 0; p < vg_ptr->pv_max; p++) {
Linus Torvalds's avatar
Linus Torvalds committed
1463
		pv_t *pvp;
Linus Torvalds's avatar
Linus Torvalds committed
1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483
		/* user space address */
		if ((pvp = vg_ptr->pv[p]) != NULL) {
			ret = lvm_do_pv_create(pvp, vg_ptr, p);
			if ( ret != 0) {
				lvm_do_vg_remove(minor);
				return ret;
			}
		}
	}

	size = vg_ptr->lv_max * sizeof(lv_t *);
	if ((snap_lv_ptr = vmalloc ( size)) == NULL) {
		printk(KERN_CRIT
		       "%s -- VG_CREATE: vmalloc error snapshot LVs at line %d\n",
		       lvm_name, __LINE__);
		lvm_do_vg_remove(minor);
		return -EFAULT;
	}
	memset(snap_lv_ptr, 0, size);

Linus Torvalds's avatar
Linus Torvalds committed
1484 1485 1486 1487 1488 1489 1490 1491
	if ((tmplv = kmalloc(sizeof(lv_t),GFP_KERNEL)) == NULL) {
		printk(KERN_CRIT
		       "%s -- VG_CREATE: kmalloc error LV at line %d\n",
		       lvm_name, __LINE__);
		vfree(snap_lv_ptr);
		return -ENOMEM;
	}

Linus Torvalds's avatar
Linus Torvalds committed
1492 1493 1494
	/* get the logical volume structures */
	vg_ptr->lv_cur = 0;
	for (l = 0; l < vg_ptr->lv_max; l++) {
Linus Torvalds's avatar
Linus Torvalds committed
1495
		lv_t *lvp;
Linus Torvalds's avatar
Linus Torvalds committed
1496
		
Linus Torvalds's avatar
Linus Torvalds committed
1497 1498
		/* user space address */
		if ((lvp = vg_ptr->lv[l]) != NULL) {
Dave Jones's avatar
Dave Jones committed
1499
			if (copy_from_user(tmplv, lvp, sizeof(userlv_t)) != 0) {
Linus Torvalds's avatar
Linus Torvalds committed
1500 1501
				P_IOCTL("ERROR: copying LV ptr %p (%d bytes)\n",
					lvp, sizeof(lv_t));
Linus Torvalds's avatar
Linus Torvalds committed
1502
				lvm_do_vg_remove(minor);
Linus Torvalds's avatar
Linus Torvalds committed
1503 1504
				vfree(snap_lv_ptr);
				kfree(tmplv);
Linus Torvalds's avatar
Linus Torvalds committed
1505 1506
				return -EFAULT;
			}
Dave Jones's avatar
Dave Jones committed
1507
			if ( tmplv->u.lv_access & LV_SNAPSHOT) {
Linus Torvalds's avatar
Linus Torvalds committed
1508 1509 1510 1511 1512 1513 1514
				snap_lv_ptr[ls] = lvp;
				vg_ptr->lv[l] = NULL;
				ls++;
				continue;
			}
			vg_ptr->lv[l] = NULL;
			/* only create original logical volumes for now */
Dave Jones's avatar
Dave Jones committed
1515
			if (lvm_do_lv_create(minor, tmplv->u.lv_name, &tmplv->u) != 0) {
Linus Torvalds's avatar
Linus Torvalds committed
1516
				lvm_do_vg_remove(minor);
Linus Torvalds's avatar
Linus Torvalds committed
1517 1518
				vfree(snap_lv_ptr);
				kfree(tmplv);
Linus Torvalds's avatar
Linus Torvalds committed
1519 1520 1521 1522 1523 1524 1525 1526
				return -EFAULT;
			}
		}
	}

	/* Second path to correct snapshot logical volumes which are not
	   in place during first path above */
	for (l = 0; l < ls; l++) {
Linus Torvalds's avatar
Linus Torvalds committed
1527
		lv_t *lvp = snap_lv_ptr[l];
Dave Jones's avatar
Dave Jones committed
1528
		if (copy_from_user(tmplv, lvp, sizeof(userlv_t)) != 0) {
Linus Torvalds's avatar
Linus Torvalds committed
1529
			lvm_do_vg_remove(minor);
Linus Torvalds's avatar
Linus Torvalds committed
1530 1531
			vfree(snap_lv_ptr);
			kfree(tmplv);
Linus Torvalds's avatar
Linus Torvalds committed
1532 1533
			return -EFAULT;
		}
Dave Jones's avatar
Dave Jones committed
1534
		if (lvm_do_lv_create(minor, tmplv->u.lv_name, &tmplv->u) != 0) {
Linus Torvalds's avatar
Linus Torvalds committed
1535
			lvm_do_vg_remove(minor);
Linus Torvalds's avatar
Linus Torvalds committed
1536 1537
			vfree(snap_lv_ptr);
			kfree(tmplv);
Linus Torvalds's avatar
Linus Torvalds committed
1538 1539 1540 1541 1542
			return -EFAULT;
		}
	}

	vfree(snap_lv_ptr);
Linus Torvalds's avatar
Linus Torvalds committed
1543
	kfree(tmplv);
Linus Torvalds's avatar
Linus Torvalds committed
1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570
	vg_count++;


	MOD_INC_USE_COUNT;

	/* let's go active */
	vg_ptr->vg_status |= VG_ACTIVE;

	return 0;
} /* lvm_do_vg_create() */


/*
 * character device support function VGDA extend
 */
static int lvm_do_vg_extend(vg_t *vg_ptr, void *arg)
{
	int ret = 0;
	uint p;
	pv_t *pv_ptr;

	if (vg_ptr == NULL) return -ENXIO;
	if (vg_ptr->pv_cur < vg_ptr->pv_max) {
		for (p = 0; p < vg_ptr->pv_max; p++) {
			if ( ( pv_ptr = vg_ptr->pv[p]) == NULL) {
				ret = lvm_do_pv_create(arg, vg_ptr, p);
				if ( ret != 0) return ret;
Linus Torvalds's avatar
Linus Torvalds committed
1571 1572
				pv_ptr = vg_ptr->pv[p];
				vg_ptr->pe_total += pv_ptr->pe_total;
Linus Torvalds's avatar
Linus Torvalds committed
1573 1574 1575 1576
				return 0;
			}
		}
	}
Linus Torvalds's avatar
Linus Torvalds committed
1577
	return -EPERM;
Linus Torvalds's avatar
Linus Torvalds committed
1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621
} /* lvm_do_vg_extend() */


/*
 * character device support function VGDA reduce
 */
static int lvm_do_vg_reduce(vg_t *vg_ptr, void *arg) {
	uint p;
	pv_t *pv_ptr;

	if (vg_ptr == NULL) return -ENXIO;
	if (copy_from_user(pv_name, arg, sizeof(pv_name)) != 0)
		return -EFAULT;

	for (p = 0; p < vg_ptr->pv_max; p++) {
		pv_ptr = vg_ptr->pv[p];
		if (pv_ptr != NULL &&
		    strcmp(pv_ptr->pv_name,
			       pv_name) == 0) {
			if (pv_ptr->lv_cur > 0) return -EPERM;
			lvm_do_pv_remove(vg_ptr, p);
			/* Make PV pointer array contiguous */
			for (; p < vg_ptr->pv_max - 1; p++)
				vg_ptr->pv[p] = vg_ptr->pv[p + 1];
			vg_ptr->pv[p + 1] = NULL;
			return 0;
		}
	}
	return -ENXIO;
} /* lvm_do_vg_reduce */


/*
 * character device support function VG rename
 */
static int lvm_do_vg_rename(vg_t *vg_ptr, void *arg)
{
	int l = 0, p = 0, len = 0;
	char vg_name[NAME_LEN] = { 0,};
	char lv_name[NAME_LEN] = { 0,};
	char *ptr = NULL;
	lv_t *lv_ptr = NULL;
	pv_t *pv_ptr = NULL;

Linus Torvalds's avatar
Linus Torvalds committed
1622 1623
	if (vg_ptr == NULL) return -ENXIO;

Linus Torvalds's avatar
Linus Torvalds committed
1624 1625 1626
	if (copy_from_user(vg_name, arg, sizeof(vg_name)) != 0)
		return -EFAULT;

Linus Torvalds's avatar
Linus Torvalds committed
1627
	lvm_fs_remove_vg(vg_ptr);
Linus Torvalds's avatar
Linus Torvalds committed
1628 1629 1630 1631 1632

	strncpy ( vg_ptr->vg_name, vg_name, sizeof ( vg_name)-1);
	for ( l = 0; l < vg_ptr->lv_max; l++)
	{
		if ((lv_ptr = vg_ptr->lv[l]) == NULL) continue;
Dave Jones's avatar
Dave Jones committed
1633 1634 1635
		strncpy(lv_ptr->u.vg_name, vg_name, sizeof ( vg_name));
		ptr = strrchr(lv_ptr->u.lv_name, '/');
		if (ptr == NULL) ptr = lv_ptr->u.lv_name;
Linus Torvalds's avatar
Linus Torvalds committed
1636 1637
		strncpy(lv_name, ptr, sizeof ( lv_name));
		len = sizeof(LVM_DIR_PREFIX);
Dave Jones's avatar
Dave Jones committed
1638 1639
		strcpy(lv_ptr->u.lv_name, LVM_DIR_PREFIX);
		strncat(lv_ptr->u.lv_name, vg_name, NAME_LEN - len);
Linus Torvalds's avatar
Linus Torvalds committed
1640
		len += strlen ( vg_name);
Dave Jones's avatar
Dave Jones committed
1641
		strncat(lv_ptr->u.lv_name, lv_name, NAME_LEN - len);
Linus Torvalds's avatar
Linus Torvalds committed
1642 1643 1644 1645 1646 1647 1648
	}
	for ( p = 0; p < vg_ptr->pv_max; p++)
	{
		if ( (pv_ptr = vg_ptr->pv[p]) == NULL) continue;
		strncpy(pv_ptr->vg_name, vg_name, NAME_LEN);
	}

Linus Torvalds's avatar
Linus Torvalds committed
1649
	lvm_fs_create_vg(vg_ptr);
Linus Torvalds's avatar
Linus Torvalds committed
1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675

	return 0;
} /* lvm_do_vg_rename */


/*
 * character device support function VGDA remove
 */
static int lvm_do_vg_remove(int minor)
{
	int i;
	vg_t *vg_ptr = vg[VG_CHR(minor)];
	pv_t *pv_ptr;

	if (vg_ptr == NULL) return -ENXIO;

#ifdef LVM_TOTAL_RESET
	if (vg_ptr->lv_open > 0 && lvm_reset_spindown == 0)
#else
	if (vg_ptr->lv_open > 0)
#endif
		return -EPERM;

	/* let's go inactive */
	vg_ptr->vg_status &= ~VG_ACTIVE;

Linus Torvalds's avatar
Linus Torvalds committed
1676 1677 1678
	/* remove from procfs and devfs */
	lvm_fs_remove_vg(vg_ptr);

Linus Torvalds's avatar
Linus Torvalds committed
1679 1680 1681 1682
	/* free LVs */
	/* first free snapshot logical volumes */
	for (i = 0; i < vg_ptr->lv_max; i++) {
		if (vg_ptr->lv[i] != NULL &&
Dave Jones's avatar
Dave Jones committed
1683
		    vg_ptr->lv[i]->u.lv_access & LV_SNAPSHOT) {
Linus Torvalds's avatar
Linus Torvalds committed
1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700
			lvm_do_lv_remove(minor, NULL, i);
			current->state = TASK_UNINTERRUPTIBLE;
			schedule_timeout(1);
		}
	}
	/* then free the rest of the LVs */
	for (i = 0; i < vg_ptr->lv_max; i++) {
		if (vg_ptr->lv[i] != NULL) {
			lvm_do_lv_remove(minor, NULL, i);
			current->state = TASK_UNINTERRUPTIBLE;
			schedule_timeout(1);
		}
	}

	/* free PVs */
	for (i = 0; i < vg_ptr->pv_max; i++) {
		if ((pv_ptr = vg_ptr->pv[i]) != NULL) {
Linus Torvalds's avatar
Linus Torvalds committed
1701
			P_KFREE("%s -- kfree %d\n", lvm_name, __LINE__);
Linus Torvalds's avatar
Linus Torvalds committed
1702 1703 1704 1705
			lvm_do_pv_remove(vg_ptr, i);
		}
	}

Linus Torvalds's avatar
Linus Torvalds committed
1706
	P_KFREE("%s -- kfree %d\n", lvm_name, __LINE__);
Linus Torvalds's avatar
Linus Torvalds committed
1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721
	kfree(vg_ptr);
	vg[VG_CHR(minor)] = NULL;

	vg_count--;

	MOD_DEC_USE_COUNT;

	return 0;
} /* lvm_do_vg_remove() */


/*
 * character device support function physical volume create
 */
static int lvm_do_pv_create(pv_t *pvp, vg_t *vg_ptr, ulong p) {
Linus Torvalds's avatar
Linus Torvalds committed
1722 1723
	pv_t *pv;
	int err;
Linus Torvalds's avatar
Linus Torvalds committed
1724

Linus Torvalds's avatar
Linus Torvalds committed
1725 1726
	pv = kmalloc(sizeof(pv_t),GFP_KERNEL);
	if (pv == NULL) {
Linus Torvalds's avatar
Linus Torvalds committed
1727
		printk(KERN_CRIT
Linus Torvalds's avatar
Linus Torvalds committed
1728
		       "%s -- PV_CREATE: kmalloc error PV at line %d\n",
Linus Torvalds's avatar
Linus Torvalds committed
1729 1730 1731
		       lvm_name, __LINE__);
		return -ENOMEM;
	}
Linus Torvalds's avatar
Linus Torvalds committed
1732 1733 1734 1735 1736 1737 1738

	memset(pv, 0, sizeof(*pv));

	if (copy_from_user(pv, pvp, sizeof(pv_t)) != 0) {
		P_IOCTL("lvm_do_pv_create ERROR: copy PV ptr %p (%d bytes)\n",
			pvp, sizeof(pv_t));
		kfree(pv);
Linus Torvalds's avatar
Linus Torvalds committed
1739 1740
		return -EFAULT;
	}
Linus Torvalds's avatar
Linus Torvalds committed
1741 1742 1743 1744 1745 1746

	if ((err = _open_pv(pv))) {
		kfree(pv);
		return err;
	}

Linus Torvalds's avatar
Linus Torvalds committed
1747 1748
	/* We don't need the PE list
	   in kernel space as with LVs pe_t list (see below) */
Linus Torvalds's avatar
Linus Torvalds committed
1749 1750 1751
	pv->pe = NULL;
	pv->pe_allocated = 0;
	pv->pv_status = PV_ACTIVE;
Linus Torvalds's avatar
Linus Torvalds committed
1752 1753
	vg_ptr->pv_act++;
	vg_ptr->pv_cur++;
Linus Torvalds's avatar
Linus Torvalds committed
1754
	lvm_fs_create_pv(vg_ptr, pv);
Linus Torvalds's avatar
Linus Torvalds committed
1755

Linus Torvalds's avatar
Linus Torvalds committed
1756
	vg_ptr->pv[p] = pv;
Linus Torvalds's avatar
Linus Torvalds committed
1757 1758 1759 1760 1761
	return 0;
} /* lvm_do_pv_create() */


/*
Linus Torvalds's avatar
Linus Torvalds committed
1762
 * character device support function physical volume remove
Linus Torvalds's avatar
Linus Torvalds committed
1763 1764
 */
static int lvm_do_pv_remove(vg_t *vg_ptr, ulong p) {
Linus Torvalds's avatar
Linus Torvalds committed
1765 1766 1767
	pv_t *pv = vg_ptr->pv[p];

	lvm_fs_remove_pv(vg_ptr, pv);
Linus Torvalds's avatar
Linus Torvalds committed
1768

Linus Torvalds's avatar
Linus Torvalds committed
1769
	vg_ptr->pe_total -= pv->pe_total;
Linus Torvalds's avatar
Linus Torvalds committed
1770 1771
	vg_ptr->pv_cur--;
	vg_ptr->pv_act--;
Linus Torvalds's avatar
Linus Torvalds committed
1772 1773 1774 1775

	_close_pv(pv);
	kfree(pv);

Linus Torvalds's avatar
Linus Torvalds committed
1776 1777 1778 1779 1780 1781
	vg_ptr->pv[p] = NULL;

	return 0;
}


Linus Torvalds's avatar
Linus Torvalds committed
1782 1783 1784 1785
static void __update_hardsectsize(lv_t *lv) {
	int le, e;
	int max_hardsectsize = 0, hardsectsize;

Dave Jones's avatar
Dave Jones committed
1786 1787
	for (le = 0; le < lv->u.lv_allocated_le; le++) {
		hardsectsize = get_hardsect_size(lv->u.lv_current_pe[le].dev);
Linus Torvalds's avatar
Linus Torvalds committed
1788 1789 1790 1791 1792 1793 1794
		if (hardsectsize == 0)
			hardsectsize = 512;
		if (hardsectsize > max_hardsectsize)
			max_hardsectsize = hardsectsize;
	}

	/* only perform this operation on active snapshots */
Dave Jones's avatar
Dave Jones committed
1795 1796 1797 1798
	if ((lv->u.lv_access & LV_SNAPSHOT) &&
	    (lv->u.lv_status & LV_ACTIVE)) {
		for (e = 0; e < lv->u.lv_remap_end; e++) {
			hardsectsize = get_hardsect_size( lv->u.lv_block_exception[e].rdev_new);
Linus Torvalds's avatar
Linus Torvalds committed
1799 1800 1801 1802 1803 1804 1805 1806
			if (hardsectsize == 0)
				hardsectsize = 512;
			if (hardsectsize > max_hardsectsize)
				max_hardsectsize = hardsectsize;
		}
	}
}

Linus Torvalds's avatar
Linus Torvalds committed
1807 1808 1809
/*
 * character device support function logical volume create
 */
Dave Jones's avatar
Dave Jones committed
1810
static int lvm_do_lv_create(int minor, char *lv_name, userlv_t *ulv)
Linus Torvalds's avatar
Linus Torvalds committed
1811
{
Linus Torvalds's avatar
Linus Torvalds committed
1812
	int e, ret, l, le, l_new, p, size, activate = 1;
Linus Torvalds's avatar
Linus Torvalds committed
1813
	ulong lv_status_save;
Dave Jones's avatar
Dave Jones committed
1814
	lv_block_exception_t *lvbe = ulv->lv_block_exception;
Linus Torvalds's avatar
Linus Torvalds committed
1815 1816
	vg_t *vg_ptr = vg[VG_CHR(minor)];
	lv_t *lv_ptr = NULL;
Linus Torvalds's avatar
Linus Torvalds committed
1817
	pe_t *pep;
Linus Torvalds's avatar
Linus Torvalds committed
1818

Dave Jones's avatar
Dave Jones committed
1819
	if (!(pep = ulv->lv_current_pe))
Linus Torvalds's avatar
Linus Torvalds committed
1820 1821
		return -EINVAL;

Dave Jones's avatar
Dave Jones committed
1822
	if (_sectors_to_k(ulv->lv_chunk_size) > LVM_SNAPSHOT_MAX_CHUNK)
Linus Torvalds's avatar
Linus Torvalds committed
1823 1824 1825
		return -EINVAL;

	for (l = 0; l < vg_ptr->lv_cur; l++) {
Linus Torvalds's avatar
Linus Torvalds committed
1826
		if (vg_ptr->lv[l] != NULL &&
Dave Jones's avatar
Dave Jones committed
1827
		    strcmp(vg_ptr->lv[l]->u.lv_name, lv_name) == 0)
Linus Torvalds's avatar
Linus Torvalds committed
1828 1829 1830 1831 1832
			return -EEXIST;
	}

	/* in case of lv_remove(), lv_create() pair */
	l_new = -1;
Dave Jones's avatar
Dave Jones committed
1833 1834
	if (vg_ptr->lv[ulv->lv_number] == NULL)
		l_new = ulv->lv_number;
Linus Torvalds's avatar
Linus Torvalds committed
1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849
	else {
		for (l = 0; l < vg_ptr->lv_max; l++) {
			if (vg_ptr->lv[l] == NULL)
				if (l_new == -1) l_new = l;
		}
	}
	if (l_new == -1) return -EPERM;
	else             l = l_new;

	if ((lv_ptr = kmalloc(sizeof(lv_t),GFP_KERNEL)) == NULL) {;
		printk(KERN_CRIT "%s -- LV_CREATE: kmalloc error LV at line %d\n",
		       lvm_name, __LINE__);
		return -ENOMEM;
	}
	/* copy preloaded LV */
Dave Jones's avatar
Dave Jones committed
1850 1851 1852 1853 1854 1855 1856 1857
	memcpy((char *) lv_ptr, (char *) ulv, sizeof(userlv_t));

	lv_status_save = lv_ptr->u.lv_status;
	lv_ptr->u.lv_status &= ~LV_ACTIVE;
	lv_ptr->u.lv_snapshot_org = NULL;
	lv_ptr->u.lv_snapshot_prev = NULL;
	lv_ptr->u.lv_snapshot_next = NULL;
	lv_ptr->u.lv_block_exception = NULL;
Linus Torvalds's avatar
Linus Torvalds committed
1858
	lv_ptr->lv_iobuf = NULL;
Dave Jones's avatar
Dave Jones committed
1859
	lv_ptr->lv_COW_table_iobuf = NULL;
Linus Torvalds's avatar
Linus Torvalds committed
1860 1861 1862
	lv_ptr->lv_snapshot_hash_table = NULL;
	lv_ptr->lv_snapshot_hash_table_size = 0;
	lv_ptr->lv_snapshot_hash_mask = 0;
Linus Torvalds's avatar
Linus Torvalds committed
1863 1864
	init_rwsem(&lv_ptr->lv_lock);

Linus Torvalds's avatar
Linus Torvalds committed
1865
	lv_ptr->lv_snapshot_use_rate = 0;
Linus Torvalds's avatar
Linus Torvalds committed
1866

Linus Torvalds's avatar
Linus Torvalds committed
1867 1868 1869
	vg_ptr->lv[l] = lv_ptr;

	/* get the PE structures from user space if this
Linus Torvalds's avatar
Linus Torvalds committed
1870
	   is not a snapshot logical volume */
Dave Jones's avatar
Dave Jones committed
1871 1872
	if (!(lv_ptr->u.lv_access & LV_SNAPSHOT)) {
		size = lv_ptr->u.lv_allocated_le * sizeof(pe_t);
Linus Torvalds's avatar
Linus Torvalds committed
1873

Dave Jones's avatar
Dave Jones committed
1874
		if ((lv_ptr->u.lv_current_pe = vmalloc(size)) == NULL) {
Linus Torvalds's avatar
Linus Torvalds committed
1875 1876 1877 1878
			printk(KERN_CRIT
			       "%s -- LV_CREATE: vmalloc error LV_CURRENT_PE of %d Byte "
			       "at line %d\n",
			       lvm_name, size, __LINE__);
Linus Torvalds's avatar
Linus Torvalds committed
1879
			P_KFREE("%s -- kfree %d\n", lvm_name, __LINE__);
Linus Torvalds's avatar
Linus Torvalds committed
1880
			kfree(lv_ptr);
Linus Torvalds's avatar
Linus Torvalds committed
1881
			vg_ptr->lv[l] = NULL;
Linus Torvalds's avatar
Linus Torvalds committed
1882 1883
			return -ENOMEM;
		}
Dave Jones's avatar
Dave Jones committed
1884
		if (copy_from_user(lv_ptr->u.lv_current_pe, pep, size)) {
Linus Torvalds's avatar
Linus Torvalds committed
1885 1886
			P_IOCTL("ERROR: copying PE ptr %p (%d bytes)\n",
				pep, sizeof(size));
Dave Jones's avatar
Dave Jones committed
1887
			vfree(lv_ptr->u.lv_current_pe);
Linus Torvalds's avatar
Linus Torvalds committed
1888 1889 1890 1891 1892
			kfree(lv_ptr);
			vg_ptr->lv[l] = NULL;
			return -EFAULT;
		}
		/* correct the PE count in PVs */
Dave Jones's avatar
Dave Jones committed
1893
		for (le = 0; le < lv_ptr->u.lv_allocated_le; le++) {
Linus Torvalds's avatar
Linus Torvalds committed
1894 1895
			vg_ptr->pe_allocated++;
			for (p = 0; p < vg_ptr->pv_cur; p++) {
Linus Torvalds's avatar
Linus Torvalds committed
1896
				if (kdev_same(vg_ptr->pv[p]->pv_dev,
Dave Jones's avatar
Dave Jones committed
1897
					      lv_ptr->u.lv_current_pe[le].dev))
Linus Torvalds's avatar
Linus Torvalds committed
1898 1899 1900 1901 1902 1903
					vg_ptr->pv[p]->pe_allocated++;
			}
		}
	} else {
		/* Get snapshot exception data and block list */
		if (lvbe != NULL) {
Dave Jones's avatar
Dave Jones committed
1904 1905 1906 1907
			lv_ptr->u.lv_snapshot_org =
			    vg_ptr->lv[LV_BLK(lv_ptr->u.lv_snapshot_minor)];
			if (lv_ptr->u.lv_snapshot_org != NULL) {
				size = lv_ptr->u.lv_remap_end * sizeof(lv_block_exception_t);
Linus Torvalds's avatar
Linus Torvalds committed
1908 1909 1910 1911 1912 1913 1914 1915 1916

				if(!size) {
					printk(KERN_WARNING
					       "%s -- zero length exception table requested\n",
					       lvm_name);
					kfree(lv_ptr);
					return -EINVAL;
				}

Dave Jones's avatar
Dave Jones committed
1917
				if ((lv_ptr->u.lv_block_exception = vmalloc(size)) == NULL) {
Linus Torvalds's avatar
Linus Torvalds committed
1918 1919 1920 1921
					printk(KERN_CRIT
					       "%s -- lvm_do_lv_create: vmalloc error LV_BLOCK_EXCEPTION "
					       "of %d byte at line %d\n",
					       lvm_name, size, __LINE__);
Linus Torvalds's avatar
Linus Torvalds committed
1922 1923
					P_KFREE("%s -- kfree %d\n", lvm_name,
						__LINE__);
Linus Torvalds's avatar
Linus Torvalds committed
1924 1925 1926 1927
					kfree(lv_ptr);
					vg_ptr->lv[l] = NULL;
					return -ENOMEM;
				}
Dave Jones's avatar
Dave Jones committed
1928 1929
				if (copy_from_user(lv_ptr->u.lv_block_exception, lvbe, size)) {
					vfree(lv_ptr->u.lv_block_exception);
Linus Torvalds's avatar
Linus Torvalds committed
1930
					kfree(lv_ptr);
Linus Torvalds's avatar
Linus Torvalds committed
1931
					vg_ptr->lv[l] = NULL;
Linus Torvalds's avatar
Linus Torvalds committed
1932 1933
					return -EFAULT;
				}
Linus Torvalds's avatar
Linus Torvalds committed
1934

Dave Jones's avatar
Dave Jones committed
1935
				if(lv_ptr->u.lv_block_exception[0].rsector_org ==
Linus Torvalds's avatar
Linus Torvalds committed
1936 1937 1938 1939 1940 1941 1942 1943 1944
				   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;
				}


Linus Torvalds's avatar
Linus Torvalds committed
1945
				/* point to the original logical volume */
Dave Jones's avatar
Dave Jones committed
1946
				lv_ptr = lv_ptr->u.lv_snapshot_org;
Linus Torvalds's avatar
Linus Torvalds committed
1947

Dave Jones's avatar
Dave Jones committed
1948 1949
				lv_ptr->u.lv_snapshot_minor = 0;
				lv_ptr->u.lv_snapshot_org = lv_ptr;
Linus Torvalds's avatar
Linus Torvalds committed
1950 1951 1952 1953
				/* our new one now back points to the previous last in the chain
				   which can be the original logical volume */
				lv_ptr = vg_ptr->lv[l];
				/* now lv_ptr points to our new last snapshot logical volume */
Dave Jones's avatar
Dave Jones committed
1954 1955 1956 1957 1958 1959 1960
				lv_ptr->u.lv_current_pe = lv_ptr->u.lv_snapshot_org->u.lv_current_pe;
				lv_ptr->lv_allocated_snapshot_le = lv_ptr->u.lv_allocated_le;
				lv_ptr->u.lv_allocated_le = lv_ptr->u.lv_snapshot_org->u.lv_allocated_le;
				lv_ptr->u.lv_current_le = lv_ptr->u.lv_snapshot_org->u.lv_current_le;
				lv_ptr->u.lv_size = lv_ptr->u.lv_snapshot_org->u.lv_size;
				lv_ptr->u.lv_stripes = lv_ptr->u.lv_snapshot_org->u.lv_stripes;
				lv_ptr->u.lv_stripesize = lv_ptr->u.lv_snapshot_org->u.lv_stripesize;
Linus Torvalds's avatar
Linus Torvalds committed
1961 1962 1963 1964

				/* Update the VG PE(s) used by snapshot reserve space. */
				vg_ptr->pe_allocated += lv_ptr->lv_allocated_snapshot_le;

Linus Torvalds's avatar
Linus Torvalds committed
1965 1966
				if ((ret = lvm_snapshot_alloc(lv_ptr)) != 0)
				{
Dave Jones's avatar
Dave Jones committed
1967
					vfree(lv_ptr->u.lv_block_exception);
Linus Torvalds's avatar
Linus Torvalds committed
1968
					kfree(lv_ptr);
Linus Torvalds's avatar
Linus Torvalds committed
1969
					vg_ptr->lv[l] = NULL;
Linus Torvalds's avatar
Linus Torvalds committed
1970 1971
					return ret;
				}
Dave Jones's avatar
Dave Jones committed
1972 1973 1974 1975
				for ( e = 0; e < lv_ptr->u.lv_remap_ptr; e++)
					lvm_hash_link (lv_ptr->u.lv_block_exception + e,
						       lv_ptr->u.lv_block_exception[e].rdev_org,
						       lv_ptr->u.lv_block_exception[e].rsector_org, lv_ptr);
Linus Torvalds's avatar
Linus Torvalds committed
1976 1977
				/* need to fill the COW exception table data
				   into the page for disk i/o */
Linus Torvalds's avatar
Linus Torvalds committed
1978 1979 1980 1981 1982
                               if(lvm_snapshot_fill_COW_page(vg_ptr, lv_ptr)) {
                                       kfree(lv_ptr);
                                       vg_ptr->lv[l] = NULL;
                                       return -EINVAL;
                               }
Linus Torvalds's avatar
Linus Torvalds committed
1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993
				init_waitqueue_head(&lv_ptr->lv_snapshot_wait);
			} else {
				kfree(lv_ptr);
				vg_ptr->lv[l] = NULL;
				return -EFAULT;
			}
		} else {
			kfree(vg_ptr->lv[l]);
			vg_ptr->lv[l] = NULL;
			return -EINVAL;
		}
Dave Jones's avatar
Dave Jones committed
1994
	} /* if ( vg[VG_CHR(minor)]->lv[l]->u.lv_access & LV_SNAPSHOT) */
Linus Torvalds's avatar
Linus Torvalds committed
1995 1996

	lv_ptr = vg_ptr->lv[l];
Dave Jones's avatar
Dave Jones committed
1997 1998 1999 2000 2001
	lvm_gendisk.part[minor(lv_ptr->u.lv_dev)].start_sect = 0;
	lvm_gendisk.part[minor(lv_ptr->u.lv_dev)].nr_sects = lv_ptr->u.lv_size;
	lvm_size[minor(lv_ptr->u.lv_dev)] = lv_ptr->u.lv_size >> 1;
	vg_lv_map[minor(lv_ptr->u.lv_dev)].vg_number = vg_ptr->vg_number;
	vg_lv_map[minor(lv_ptr->u.lv_dev)].lv_number = lv_ptr->u.lv_number;
Linus Torvalds's avatar
Linus Torvalds committed
2002
	vg_ptr->lv_cur++;
Dave Jones's avatar
Dave Jones committed
2003
	lv_ptr->u.lv_status = lv_status_save;
Linus Torvalds's avatar
Linus Torvalds committed
2004

Linus Torvalds's avatar
Linus Torvalds committed
2005
	__update_hardsectsize(lv_ptr);
Linus Torvalds's avatar
Linus Torvalds committed
2006 2007

	/* optionally add our new snapshot LV */
Dave Jones's avatar
Dave Jones committed
2008 2009
	if (lv_ptr->u.lv_access & LV_SNAPSHOT) {
		lv_t *org = lv_ptr->u.lv_snapshot_org, *last;
Linus Torvalds's avatar
Linus Torvalds committed
2010

Linus Torvalds's avatar
Linus Torvalds committed
2011
		/* sync the original logical volume */
Dave Jones's avatar
Dave Jones committed
2012
		fsync_dev(org->u.lv_dev);
Linus Torvalds's avatar
Linus Torvalds committed
2013 2014
#ifdef	LVM_VFS_ENHANCEMENT
		/* VFS function call to sync and lock the filesystem */
Dave Jones's avatar
Dave Jones committed
2015
		fsync_dev_lockfs(org->u.lv_dev);
Linus Torvalds's avatar
Linus Torvalds committed
2016
#endif
Linus Torvalds's avatar
Linus Torvalds committed
2017

Linus Torvalds's avatar
Linus Torvalds committed
2018
		down_write(&org->lv_lock);
Dave Jones's avatar
Dave Jones committed
2019 2020
		org->u.lv_access |= LV_SNAPSHOT_ORG;
		lv_ptr->u.lv_access &= ~LV_SNAPSHOT_ORG; /* this can only hide an userspace bug */
Linus Torvalds's avatar
Linus Torvalds committed
2021 2022

		/* Link in the list of snapshot volumes */
Dave Jones's avatar
Dave Jones committed
2023 2024 2025
		for (last = org; last->u.lv_snapshot_next; last = last->u.lv_snapshot_next);
		lv_ptr->u.lv_snapshot_prev = last;
		last->u.lv_snapshot_next = lv_ptr;
Linus Torvalds's avatar
Linus Torvalds committed
2026
		up_write(&org->lv_lock);
Linus Torvalds's avatar
Linus Torvalds committed
2027 2028 2029
	}

	/* activate the logical volume */
Linus Torvalds's avatar
Linus Torvalds committed
2030
	if(activate)
Dave Jones's avatar
Dave Jones committed
2031
		lv_ptr->u.lv_status |= LV_ACTIVE;
Linus Torvalds's avatar
Linus Torvalds committed
2032
	else
Dave Jones's avatar
Dave Jones committed
2033
		lv_ptr->u.lv_status &= ~LV_ACTIVE;
Linus Torvalds's avatar
Linus Torvalds committed
2034

Dave Jones's avatar
Dave Jones committed
2035 2036
	if ( lv_ptr->u.lv_access & LV_WRITE)
		set_device_ro(lv_ptr->u.lv_dev, 0);
Linus Torvalds's avatar
Linus Torvalds committed
2037
	else
Dave Jones's avatar
Dave Jones committed
2038
		set_device_ro(lv_ptr->u.lv_dev, 1);
Linus Torvalds's avatar
Linus Torvalds committed
2039 2040 2041

#ifdef	LVM_VFS_ENHANCEMENT
/* VFS function call to unlock the filesystem */
Dave Jones's avatar
Dave Jones committed
2042 2043
	if (lv_ptr->u.lv_access & LV_SNAPSHOT)
		unlockfs(lv_ptr->u.lv_snapshot_org->u.lv_dev);
Linus Torvalds's avatar
Linus Torvalds committed
2044 2045 2046 2047
#endif

	lv_ptr->vg = vg_ptr;

Dave Jones's avatar
Dave Jones committed
2048
	lvm_gendisk.part[minor(lv_ptr->u.lv_dev)].de =
Linus Torvalds's avatar
Linus Torvalds committed
2049 2050
		lvm_fs_create_lv(vg_ptr, lv_ptr);

Linus Torvalds's avatar
Linus Torvalds committed
2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066
	return 0;
} /* lvm_do_lv_create() */


/*
 * character device support function logical volume remove
 */
static int lvm_do_lv_remove(int minor, char *lv_name, int l)
{
	uint le, p;
	vg_t *vg_ptr = vg[VG_CHR(minor)];
	lv_t *lv_ptr;

	if (l == -1) {
		for (l = 0; l < vg_ptr->lv_max; l++) {
			if (vg_ptr->lv[l] != NULL &&
Dave Jones's avatar
Dave Jones committed
2067
			    strcmp(vg_ptr->lv[l]->u.lv_name, lv_name) == 0) {
Linus Torvalds's avatar
Linus Torvalds committed
2068 2069 2070 2071 2072 2073 2074 2075
				break;
			}
		}
	}
	if (l == vg_ptr->lv_max) return -ENXIO;

	lv_ptr = vg_ptr->lv[l];
#ifdef LVM_TOTAL_RESET
Dave Jones's avatar
Dave Jones committed
2076
	if (lv_ptr->u.lv_open > 0 && lvm_reset_spindown == 0)
Linus Torvalds's avatar
Linus Torvalds committed
2077
#else
Dave Jones's avatar
Dave Jones committed
2078
	if (lv_ptr->u.lv_open > 0)
Linus Torvalds's avatar
Linus Torvalds committed
2079 2080 2081 2082 2083
#endif
		return -EBUSY;

	/* check for deletion of snapshot source while
	   snapshot volume still exists */
Dave Jones's avatar
Dave Jones committed
2084 2085
	if ((lv_ptr->u.lv_access & LV_SNAPSHOT_ORG) &&
	    lv_ptr->u.lv_snapshot_next != NULL)
Linus Torvalds's avatar
Linus Torvalds committed
2086 2087
		return -EPERM;

Linus Torvalds's avatar
Linus Torvalds committed
2088 2089
	lvm_fs_remove_lv(vg_ptr, lv_ptr);

Dave Jones's avatar
Dave Jones committed
2090
	if (lv_ptr->u.lv_access & LV_SNAPSHOT) {
Linus Torvalds's avatar
Linus Torvalds committed
2091 2092 2093 2094
		/*
		 * Atomically make the the snapshot invisible
		 * to the original lv before playing with it.
		 */
Dave Jones's avatar
Dave Jones committed
2095
		lv_t * org = lv_ptr->u.lv_snapshot_org;
Linus Torvalds's avatar
Linus Torvalds committed
2096
		down_write(&org->lv_lock);
Linus Torvalds's avatar
Linus Torvalds committed
2097 2098

		/* remove this snapshot logical volume from the chain */
Dave Jones's avatar
Dave Jones committed
2099 2100 2101 2102
		lv_ptr->u.lv_snapshot_prev->u.lv_snapshot_next = lv_ptr->u.lv_snapshot_next;
		if (lv_ptr->u.lv_snapshot_next != NULL) {
			lv_ptr->u.lv_snapshot_next->u.lv_snapshot_prev =
			    lv_ptr->u.lv_snapshot_prev;
Linus Torvalds's avatar
Linus Torvalds committed
2103 2104 2105
		}

		/* no more snapshots? */
Dave Jones's avatar
Dave Jones committed
2106 2107
		if (!org->u.lv_snapshot_next) {
			org->u.lv_access &= ~LV_SNAPSHOT_ORG;
Linus Torvalds's avatar
Linus Torvalds committed
2108 2109 2110
		}
		up_write(&org->lv_lock);

Linus Torvalds's avatar
Linus Torvalds committed
2111 2112 2113 2114 2115 2116
		lvm_snapshot_release(lv_ptr);

		/* Update the VG PE(s) used by snapshot reserve space. */
		vg_ptr->pe_allocated -= lv_ptr->lv_allocated_snapshot_le;
	}

Dave Jones's avatar
Dave Jones committed
2117
	lv_ptr->u.lv_status |= LV_SPINDOWN;
Linus Torvalds's avatar
Linus Torvalds committed
2118 2119

	/* sync the buffers */
Dave Jones's avatar
Dave Jones committed
2120
	fsync_dev(lv_ptr->u.lv_dev);
Linus Torvalds's avatar
Linus Torvalds committed
2121

Dave Jones's avatar
Dave Jones committed
2122
	lv_ptr->u.lv_status &= ~LV_ACTIVE;
Linus Torvalds's avatar
Linus Torvalds committed
2123 2124

	/* invalidate the buffers */
Dave Jones's avatar
Dave Jones committed
2125
	invalidate_buffers(lv_ptr->u.lv_dev);
Linus Torvalds's avatar
Linus Torvalds committed
2126 2127

	/* reset generic hd */
Dave Jones's avatar
Dave Jones committed
2128 2129 2130 2131
	lvm_gendisk.part[minor(lv_ptr->u.lv_dev)].start_sect = -1;
	lvm_gendisk.part[minor(lv_ptr->u.lv_dev)].nr_sects = 0;
	lvm_gendisk.part[minor(lv_ptr->u.lv_dev)].de = 0;
	lvm_size[minor(lv_ptr->u.lv_dev)] = 0;
Linus Torvalds's avatar
Linus Torvalds committed
2132 2133

	/* reset VG/LV mapping */
Dave Jones's avatar
Dave Jones committed
2134 2135
	vg_lv_map[minor(lv_ptr->u.lv_dev)].vg_number = ABS_MAX_VG;
	vg_lv_map[minor(lv_ptr->u.lv_dev)].lv_number = -1;
Linus Torvalds's avatar
Linus Torvalds committed
2136

Linus Torvalds's avatar
Linus Torvalds committed
2137 2138
	/* correct the PE count in PVs if this is not a snapshot
           logical volume */
Dave Jones's avatar
Dave Jones committed
2139
	if (!(lv_ptr->u.lv_access & LV_SNAPSHOT)) {
Linus Torvalds's avatar
Linus Torvalds committed
2140
		/* only if this is no snapshot logical volume because
Dave Jones's avatar
Dave Jones committed
2141
		   we share the u.lv_current_pe[] structs with the
Linus Torvalds's avatar
Linus Torvalds committed
2142
		   original logical volume */
Dave Jones's avatar
Dave Jones committed
2143
		for (le = 0; le < lv_ptr->u.lv_allocated_le; le++) {
Linus Torvalds's avatar
Linus Torvalds committed
2144 2145
			vg_ptr->pe_allocated--;
			for (p = 0; p < vg_ptr->pv_cur; p++) {
Linus Torvalds's avatar
Linus Torvalds committed
2146
				if (kdev_same(vg_ptr->pv[p]->pv_dev,
Dave Jones's avatar
Dave Jones committed
2147
					      lv_ptr->u.lv_current_pe[le].dev))
Linus Torvalds's avatar
Linus Torvalds committed
2148 2149 2150
					vg_ptr->pv[p]->pe_allocated--;
			}
		}
Dave Jones's avatar
Dave Jones committed
2151
		vfree(lv_ptr->u.lv_current_pe);
Linus Torvalds's avatar
Linus Torvalds committed
2152 2153
	}

Linus Torvalds's avatar
Linus Torvalds committed
2154
	P_KFREE("%s -- kfree %d\n", lvm_name, __LINE__);
Linus Torvalds's avatar
Linus Torvalds committed
2155 2156 2157 2158 2159 2160 2161 2162
	kfree(lv_ptr);
	vg_ptr->lv[l] = NULL;
	vg_ptr->lv_cur--;
	return 0;
} /* lvm_do_lv_remove() */


/*
Linus Torvalds's avatar
Linus Torvalds committed
2163
 * logical volume extend / reduce
Linus Torvalds's avatar
Linus Torvalds committed
2164
 */
Linus Torvalds's avatar
Linus Torvalds committed
2165 2166 2167 2168
static int __extend_reduce_snapshot(vg_t *vg_ptr, lv_t *old_lv, lv_t *new_lv) {
        ulong size;
        lv_block_exception_t *lvbe;

Dave Jones's avatar
Dave Jones committed
2169
        if (!new_lv->u.lv_block_exception)
Linus Torvalds's avatar
Linus Torvalds committed
2170 2171
                return -ENXIO;

Dave Jones's avatar
Dave Jones committed
2172
        size = new_lv->u.lv_remap_end * sizeof(lv_block_exception_t);
Linus Torvalds's avatar
Linus Torvalds committed
2173 2174 2175 2176 2177 2178 2179
        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;
        }
Linus Torvalds's avatar
Linus Torvalds committed
2180

Dave Jones's avatar
Dave Jones committed
2181 2182
        if ((new_lv->u.lv_remap_end > old_lv->u.lv_remap_end) &&
            (copy_from_user(lvbe, new_lv->u.lv_block_exception, size))) {
Linus Torvalds's avatar
Linus Torvalds committed
2183 2184 2185
                vfree(lvbe);
                return -EFAULT;
        }
Dave Jones's avatar
Dave Jones committed
2186
        new_lv->u.lv_block_exception = lvbe;
Linus Torvalds's avatar
Linus Torvalds committed
2187

Linus Torvalds's avatar
Linus Torvalds committed
2188
        if (lvm_snapshot_alloc_hash_table(new_lv)) {
Dave Jones's avatar
Dave Jones committed
2189
                vfree(new_lv->u.lv_block_exception);
Linus Torvalds's avatar
Linus Torvalds committed
2190 2191
                return -ENOMEM;
        }
Linus Torvalds's avatar
Linus Torvalds committed
2192

Linus Torvalds's avatar
Linus Torvalds committed
2193 2194
        return 0;
}
Linus Torvalds's avatar
Linus Torvalds committed
2195

Linus Torvalds's avatar
Linus Torvalds committed
2196 2197 2198 2199 2200
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 */
Dave Jones's avatar
Dave Jones committed
2201
        size = new_lv->u.lv_current_le * sizeof(pe_t);
Linus Torvalds's avatar
Linus Torvalds committed
2202 2203 2204 2205 2206 2207 2208
        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",
                       lvm_name, size, __LINE__);
                return -ENOMEM;
        }
Linus Torvalds's avatar
Linus Torvalds committed
2209

Linus Torvalds's avatar
Linus Torvalds committed
2210
        /* get the PE structures from user space */
Dave Jones's avatar
Dave Jones committed
2211 2212
        if (copy_from_user(pe, new_lv->u.lv_current_pe, size)) {
                if(old_lv->u.lv_access & LV_SNAPSHOT)
Linus Torvalds's avatar
Linus Torvalds committed
2213 2214 2215 2216
                        vfree(new_lv->lv_snapshot_hash_table);
                vfree(pe);
                return -EFAULT;
        }
Linus Torvalds's avatar
Linus Torvalds committed
2217

Dave Jones's avatar
Dave Jones committed
2218
        new_lv->u.lv_current_pe = pe;
Linus Torvalds's avatar
Linus Torvalds committed
2219

Linus Torvalds's avatar
Linus Torvalds committed
2220
        /* reduce allocation counters on PV(s) */
Dave Jones's avatar
Dave Jones committed
2221
        for (l = 0; l < old_lv->u.lv_allocated_le; l++) {
Linus Torvalds's avatar
Linus Torvalds committed
2222 2223
                vg_ptr->pe_allocated--;
                for (p = 0; p < vg_ptr->pv_cur; p++) {
Linus Torvalds's avatar
Linus Torvalds committed
2224
			if (kdev_same(vg_ptr->pv[p]->pv_dev,
Dave Jones's avatar
Dave Jones committed
2225
				      old_lv->u.lv_current_pe[l].dev)) {
Linus Torvalds's avatar
Linus Torvalds committed
2226 2227 2228 2229 2230
                                vg_ptr->pv[p]->pe_allocated--;
                                break;
                        }
                }
        }
Linus Torvalds's avatar
Linus Torvalds committed
2231

Linus Torvalds's avatar
Linus Torvalds committed
2232
        /* extend the PE count in PVs */
Dave Jones's avatar
Dave Jones committed
2233
        for (l = 0; l < new_lv->u.lv_allocated_le; l++) {
Linus Torvalds's avatar
Linus Torvalds committed
2234 2235
                vg_ptr->pe_allocated++;
                for (p = 0; p < vg_ptr->pv_cur; p++) {
Linus Torvalds's avatar
Linus Torvalds committed
2236
			if (kdev_same(vg_ptr->pv[p]->pv_dev,
Dave Jones's avatar
Dave Jones committed
2237
				      new_lv->u.lv_current_pe[l].dev)) {
Linus Torvalds's avatar
Linus Torvalds committed
2238 2239 2240 2241 2242
                                vg_ptr->pv[p]->pe_allocated++;
                                break;
                        }
                }
        }
Linus Torvalds's avatar
Linus Torvalds committed
2243

Linus Torvalds's avatar
Linus Torvalds committed
2244
        /* save availiable i/o statistic data */
Dave Jones's avatar
Dave Jones committed
2245 2246
        if (old_lv->u.lv_stripes < 2) {   /* linear logical volume */
                end = min(old_lv->u.lv_current_le, new_lv->u.lv_current_le);
Linus Torvalds's avatar
Linus Torvalds committed
2247
                for (l = 0; l < end; l++) {
Dave Jones's avatar
Dave Jones committed
2248 2249
                        new_lv->u.lv_current_pe[l].reads +=
                                old_lv->u.lv_current_pe[l].reads;
Linus Torvalds's avatar
Linus Torvalds committed
2250

Dave Jones's avatar
Dave Jones committed
2251 2252
                        new_lv->u.lv_current_pe[l].writes +=
                                old_lv->u.lv_current_pe[l].writes;
Linus Torvalds's avatar
Linus Torvalds committed
2253
                }
Linus Torvalds's avatar
Linus Torvalds committed
2254

Linus Torvalds's avatar
Linus Torvalds committed
2255 2256
        } else {                /* striped logical volume */
                uint i, j, source, dest, end, old_stripe_size, new_stripe_size;
Linus Torvalds's avatar
Linus Torvalds committed
2257

Dave Jones's avatar
Dave Jones committed
2258 2259
                old_stripe_size = old_lv->u.lv_allocated_le / old_lv->u.lv_stripes;
                new_stripe_size = new_lv->u.lv_allocated_le / new_lv->u.lv_stripes;
Linus Torvalds's avatar
Linus Torvalds committed
2260
                end = min(old_stripe_size, new_stripe_size);
Linus Torvalds's avatar
Linus Torvalds committed
2261

Linus Torvalds's avatar
Linus Torvalds committed
2262
                for (i = source = dest = 0;
Dave Jones's avatar
Dave Jones committed
2263
                     i < new_lv->u.lv_stripes; i++) {
Linus Torvalds's avatar
Linus Torvalds committed
2264
                        for (j = 0; j < end; j++) {
Dave Jones's avatar
Dave Jones committed
2265 2266 2267 2268
                                new_lv->u.lv_current_pe[dest + j].reads +=
                                    old_lv->u.lv_current_pe[source + j].reads;
                                new_lv->u.lv_current_pe[dest + j].writes +=
                                    old_lv->u.lv_current_pe[source + j].writes;
Linus Torvalds's avatar
Linus Torvalds committed
2269 2270 2271 2272 2273
                        }
                        source += old_stripe_size;
                        dest += new_stripe_size;
                }
        }
Linus Torvalds's avatar
Linus Torvalds committed
2274

Linus Torvalds's avatar
Linus Torvalds committed
2275 2276
        return 0;
}
Linus Torvalds's avatar
Linus Torvalds committed
2277

Dave Jones's avatar
Dave Jones committed
2278
static int lvm_do_lv_extend_reduce(int minor, char *lv_name, userlv_t *ulv)
Linus Torvalds's avatar
Linus Torvalds committed
2279 2280 2281 2282 2283
{
        int r;
        ulong l, e, size;
        vg_t *vg_ptr = vg[VG_CHR(minor)];
        lv_t *old_lv;
Dave Jones's avatar
Dave Jones committed
2284
	lv_t *new_lv;
Linus Torvalds's avatar
Linus Torvalds committed
2285 2286
        pe_t *pe;

Dave Jones's avatar
Dave Jones committed
2287 2288 2289 2290 2291 2292 2293 2294 2295 2296
	if((new_lv = kmalloc(sizeof(lv_t),GFP_KERNEL)) == NULL){
		printk(KERN_CRIT 
		       "%s -- LV_EXTEND/REDUCE: kmallor error LV at line %d\n",
		       lvm_name,__LINE__);
		return -ENOMEM;
	}
	memset(new_lv,0,sizeof(lv_t));
	memcpy(&new_lv->u,ulv,sizeof(userlv_t));

        if ((pe = new_lv->u.lv_current_pe) == NULL)
Linus Torvalds's avatar
Linus Torvalds committed
2297 2298 2299
                return -EINVAL;

        for (l = 0; l < vg_ptr->lv_max; l++)
Dave Jones's avatar
Dave Jones committed
2300
                if (vg_ptr->lv[l] && !strcmp(vg_ptr->lv[l]->u.lv_name, lv_name))
Linus Torvalds's avatar
Linus Torvalds committed
2301 2302 2303 2304 2305 2306 2307
                        break;

        if (l == vg_ptr->lv_max)
                return -ENXIO;

        old_lv = vg_ptr->lv[l];

Dave Jones's avatar
Dave Jones committed
2308
	if (old_lv->u.lv_access & LV_SNAPSHOT) {
Linus Torvalds's avatar
Linus Torvalds committed
2309
		/* only perform this operation on active snapshots */
Dave Jones's avatar
Dave Jones committed
2310
		if (old_lv->u.lv_status & LV_ACTIVE)
Linus Torvalds's avatar
Linus Torvalds committed
2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323
                r = __extend_reduce_snapshot(vg_ptr, old_lv, new_lv);
        else
			r = -EPERM;

	} else
                r = __extend_reduce(vg_ptr, old_lv, new_lv);

        if(r)
                return r;

        /* copy relevent fields */
	down_write(&old_lv->lv_lock);

Dave Jones's avatar
Dave Jones committed
2324 2325 2326
        if(new_lv->u.lv_access & LV_SNAPSHOT) {
                size = (new_lv->u.lv_remap_end > old_lv->u.lv_remap_end) ?
                        old_lv->u.lv_remap_ptr : new_lv->u.lv_remap_end;
Linus Torvalds's avatar
Linus Torvalds committed
2327
                size *= sizeof(lv_block_exception_t);
Dave Jones's avatar
Dave Jones committed
2328 2329
                memcpy(new_lv->u.lv_block_exception,
                       old_lv->u.lv_block_exception, size);
Linus Torvalds's avatar
Linus Torvalds committed
2330

Dave Jones's avatar
Dave Jones committed
2331 2332
                old_lv->u.lv_remap_end = new_lv->u.lv_remap_end;
                old_lv->u.lv_block_exception = new_lv->u.lv_block_exception;
Linus Torvalds's avatar
Linus Torvalds committed
2333 2334 2335 2336 2337 2338 2339
                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;

Dave Jones's avatar
Dave Jones committed
2340 2341 2342 2343
                for (e = 0; e < new_lv->u.lv_remap_ptr; e++)
                        lvm_hash_link(new_lv->u.lv_block_exception + e,
                                      new_lv->u.lv_block_exception[e].rdev_org,
                                    new_lv->u.lv_block_exception[e].rsector_org,
Linus Torvalds's avatar
Linus Torvalds committed
2344 2345 2346 2347
                                      new_lv);

        } else {

Dave Jones's avatar
Dave Jones committed
2348
                vfree(old_lv->u.lv_current_pe);
Linus Torvalds's avatar
Linus Torvalds committed
2349 2350
                vfree(old_lv->lv_snapshot_hash_table);

Dave Jones's avatar
Dave Jones committed
2351 2352 2353 2354 2355 2356 2357
                old_lv->u.lv_size = new_lv->u.lv_size;
                old_lv->u.lv_allocated_le = new_lv->u.lv_allocated_le;
                old_lv->u.lv_current_le = new_lv->u.lv_current_le;
                old_lv->u.lv_current_pe = new_lv->u.lv_current_pe;
                lvm_gendisk.part[minor(old_lv->u.lv_dev)].nr_sects =
                        old_lv->u.lv_size;
                lvm_size[minor(old_lv->u.lv_dev)] = old_lv->u.lv_size >> 1;
Linus Torvalds's avatar
Linus Torvalds committed
2358

Dave Jones's avatar
Dave Jones committed
2359
                if (old_lv->u.lv_access & LV_SNAPSHOT_ORG) {
Linus Torvalds's avatar
Linus Torvalds committed
2360
                        lv_t *snap;
Dave Jones's avatar
Dave Jones committed
2361 2362
                        for(snap = old_lv->u.lv_snapshot_next; snap;
                            snap = snap->u.lv_snapshot_next) {
Linus Torvalds's avatar
Linus Torvalds committed
2363
				down_write(&snap->lv_lock);
Dave Jones's avatar
Dave Jones committed
2364 2365 2366 2367 2368 2369 2370 2371 2372 2373
                                snap->u.lv_current_pe = old_lv->u.lv_current_pe;
                                snap->u.lv_allocated_le =
                                        old_lv->u.lv_allocated_le;
                                snap->u.lv_current_le = old_lv->u.lv_current_le;
                                snap->u.lv_size = old_lv->u.lv_size;

                                lvm_gendisk.part[minor(snap->u.lv_dev)].nr_sects
                                        = old_lv->u.lv_size;
                                lvm_size[minor(snap->u.lv_dev)] =
                                        old_lv->u.lv_size >> 1;
Linus Torvalds's avatar
Linus Torvalds committed
2374 2375 2376 2377 2378
                                __update_hardsectsize(snap);
				up_write(&snap->lv_lock);
                        }
                }
        }
Linus Torvalds's avatar
Linus Torvalds committed
2379

Linus Torvalds's avatar
Linus Torvalds committed
2380 2381
        __update_hardsectsize(old_lv);
	up_write(&old_lv->lv_lock);
Linus Torvalds's avatar
Linus Torvalds committed
2382

Linus Torvalds's avatar
Linus Torvalds committed
2383
        return 0;
Linus Torvalds's avatar
Linus Torvalds committed
2384 2385 2386 2387 2388 2389 2390 2391 2392 2393
} /* lvm_do_lv_extend_reduce() */


/*
 * character device support function logical volume status by name
 */
static int lvm_do_lv_status_byname(vg_t *vg_ptr, void *arg)
{
	uint l;
	lv_status_byname_req_t lv_status_byname_req;
Linus Torvalds's avatar
Linus Torvalds committed
2394 2395 2396
	void *saved_ptr1;
	void *saved_ptr2;
	lv_t *lv_ptr;
Linus Torvalds's avatar
Linus Torvalds committed
2397 2398 2399 2400 2401 2402 2403 2404 2405

	if (vg_ptr == NULL) return -ENXIO;
	if (copy_from_user(&lv_status_byname_req, arg,
			   sizeof(lv_status_byname_req_t)) != 0)
		return -EFAULT;

	if (lv_status_byname_req.lv == NULL) return -EINVAL;

	for (l = 0; l < vg_ptr->lv_max; l++) {
Linus Torvalds's avatar
Linus Torvalds committed
2406
		if ((lv_ptr = vg_ptr->lv[l]) != NULL &&
Dave Jones's avatar
Dave Jones committed
2407
		    strcmp(lv_ptr->u.lv_name,
Linus Torvalds's avatar
Linus Torvalds committed
2408 2409
			   lv_status_byname_req.lv_name) == 0) {
		        /* Save usermode pointers */
Dave Jones's avatar
Dave Jones committed
2410
		        if (copy_from_user(&saved_ptr1, &lv_status_byname_req.lv->u.lv_current_pe, sizeof(void*)) != 0)
Linus Torvalds's avatar
Linus Torvalds committed
2411
				return -EFAULT;
Dave Jones's avatar
Dave Jones committed
2412
			if (copy_from_user(&saved_ptr2, &lv_status_byname_req.lv->u.lv_block_exception, sizeof(void*)) != 0)
Linus Torvalds's avatar
Linus Torvalds committed
2413 2414
			        return -EFAULT;
		        if (copy_to_user(lv_status_byname_req.lv,
Linus Torvalds's avatar
Linus Torvalds committed
2415
					 lv_ptr,
Dave Jones's avatar
Dave Jones committed
2416
					 sizeof(userlv_t)) != 0)
Linus Torvalds's avatar
Linus Torvalds committed
2417 2418
				return -EFAULT;

Linus Torvalds's avatar
Linus Torvalds committed
2419 2420
			if (saved_ptr1 != NULL) {
				if (copy_to_user(saved_ptr1,
Dave Jones's avatar
Dave Jones committed
2421 2422
						 lv_ptr->u.lv_current_pe,
						 lv_ptr->u.lv_allocated_le *
Linus Torvalds's avatar
Linus Torvalds committed
2423
				       		 sizeof(pe_t)) != 0)
Linus Torvalds's avatar
Linus Torvalds committed
2424 2425
					return -EFAULT;
			}
Linus Torvalds's avatar
Linus Torvalds committed
2426
			/* Restore usermode pointers */
Dave Jones's avatar
Dave Jones committed
2427
			if (copy_to_user(&lv_status_byname_req.lv->u.lv_current_pe, &saved_ptr1, sizeof(void*)) != 0)
Linus Torvalds's avatar
Linus Torvalds committed
2428
			        return -EFAULT;
Linus Torvalds's avatar
Linus Torvalds committed
2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441
			return 0;
		}
	}
	return -ENXIO;
} /* lvm_do_lv_status_byname() */


/*
 * character device support function logical volume status by index
 */
static int lvm_do_lv_status_byindex(vg_t *vg_ptr,void *arg)
{
	lv_status_byindex_req_t lv_status_byindex_req;
Linus Torvalds's avatar
Linus Torvalds committed
2442 2443 2444
	void *saved_ptr1;
	void *saved_ptr2;
	lv_t *lv_ptr;
Linus Torvalds's avatar
Linus Torvalds committed
2445 2446 2447 2448 2449 2450

	if (vg_ptr == NULL) return -ENXIO;
	if (copy_from_user(&lv_status_byindex_req, arg,
			   sizeof(lv_status_byindex_req)) != 0)
		return -EFAULT;

Linus Torvalds's avatar
Linus Torvalds committed
2451 2452 2453 2454
	if (lv_status_byindex_req.lv == NULL)
		return -EINVAL;
	if (lv_status_byindex_req.lv_index <0 ||
		lv_status_byindex_req.lv_index >= MAX_LV)
Linus Torvalds's avatar
Linus Torvalds committed
2455 2456 2457 2458
		return -EINVAL;
	if ( ( lv_ptr = vg_ptr->lv[lv_status_byindex_req.lv_index]) == NULL)
		return -ENXIO;

Linus Torvalds's avatar
Linus Torvalds committed
2459
	/* Save usermode pointers */
Dave Jones's avatar
Dave Jones committed
2460
	if (copy_from_user(&saved_ptr1, &lv_status_byindex_req.lv->u.lv_current_pe, sizeof(void*)) != 0)
Linus Torvalds's avatar
Linus Torvalds committed
2461
	        return -EFAULT;
Dave Jones's avatar
Dave Jones committed
2462
	if (copy_from_user(&saved_ptr2, &lv_status_byindex_req.lv->u.lv_block_exception, sizeof(void*)) != 0)
Linus Torvalds's avatar
Linus Torvalds committed
2463
	        return -EFAULT;
Linus Torvalds's avatar
Linus Torvalds committed
2464

Dave Jones's avatar
Dave Jones committed
2465
	if (copy_to_user(lv_status_byindex_req.lv, lv_ptr, sizeof(userlv_t)) != 0)
Linus Torvalds's avatar
Linus Torvalds committed
2466
		return -EFAULT;
Linus Torvalds's avatar
Linus Torvalds committed
2467 2468
	if (saved_ptr1 != NULL) {
		if (copy_to_user(saved_ptr1,
Dave Jones's avatar
Dave Jones committed
2469 2470
				 lv_ptr->u.lv_current_pe,
				 lv_ptr->u.lv_allocated_le *
Linus Torvalds's avatar
Linus Torvalds committed
2471
		       		 sizeof(pe_t)) != 0)
Linus Torvalds's avatar
Linus Torvalds committed
2472 2473
			return -EFAULT;
	}
Linus Torvalds's avatar
Linus Torvalds committed
2474 2475

	/* Restore usermode pointers */
Dave Jones's avatar
Dave Jones committed
2476
	if (copy_to_user(&lv_status_byindex_req.lv->u.lv_current_pe, &saved_ptr1, sizeof(void *)) != 0)
Linus Torvalds's avatar
Linus Torvalds committed
2477 2478
	        return -EFAULT;

Linus Torvalds's avatar
Linus Torvalds committed
2479 2480 2481 2482 2483 2484 2485 2486 2487 2488
	return 0;
} /* lvm_do_lv_status_byindex() */


/*
 * character device support function logical volume status by device number
 */
static int lvm_do_lv_status_bydev(vg_t * vg_ptr, void * arg) {
	int l;
	lv_status_bydev_req_t lv_status_bydev_req;
Linus Torvalds's avatar
Linus Torvalds committed
2489 2490 2491
	void *saved_ptr1;
	void *saved_ptr2;
	lv_t *lv_ptr;
Linus Torvalds's avatar
Linus Torvalds committed
2492 2493 2494 2495 2496 2497 2498 2499

	if (vg_ptr == NULL) return -ENXIO;
	if (copy_from_user(&lv_status_bydev_req, arg,
			   sizeof(lv_status_bydev_req)) != 0)
		return -EFAULT;

	for ( l = 0; l < vg_ptr->lv_max; l++) {
		if ( vg_ptr->lv[l] == NULL) continue;
Dave Jones's avatar
Dave Jones committed
2500
		if ( kdev_same(vg_ptr->lv[l]->u.lv_dev,
Linus Torvalds's avatar
Linus Torvalds committed
2501 2502
			       to_kdev_t(lv_status_bydev_req.dev)))
			break;
Linus Torvalds's avatar
Linus Torvalds committed
2503 2504 2505
	}

	if ( l == vg_ptr->lv_max) return -ENXIO;
Linus Torvalds's avatar
Linus Torvalds committed
2506 2507 2508
	lv_ptr = vg_ptr->lv[l];

	/* Save usermode pointers */
Dave Jones's avatar
Dave Jones committed
2509
	if (copy_from_user(&saved_ptr1, &lv_status_bydev_req.lv->u.lv_current_pe, sizeof(void*)) != 0)
Linus Torvalds's avatar
Linus Torvalds committed
2510
	        return -EFAULT;
Dave Jones's avatar
Dave Jones committed
2511
	if (copy_from_user(&saved_ptr2, &lv_status_bydev_req.lv->u.lv_block_exception, sizeof(void*)) != 0)
Linus Torvalds's avatar
Linus Torvalds committed
2512
	        return -EFAULT;
Linus Torvalds's avatar
Linus Torvalds committed
2513

Linus Torvalds's avatar
Linus Torvalds committed
2514
	if (copy_to_user(lv_status_bydev_req.lv, lv_ptr, sizeof(lv_t)) != 0)
Linus Torvalds's avatar
Linus Torvalds committed
2515
		return -EFAULT;
Linus Torvalds's avatar
Linus Torvalds committed
2516 2517
	if (saved_ptr1 != NULL) {
		if (copy_to_user(saved_ptr1,
Dave Jones's avatar
Dave Jones committed
2518 2519
				 lv_ptr->u.lv_current_pe,
				 lv_ptr->u.lv_allocated_le *
Linus Torvalds's avatar
Linus Torvalds committed
2520 2521 2522 2523
		       		 sizeof(pe_t)) != 0)
			return -EFAULT;
	}
	/* Restore usermode pointers */
Dave Jones's avatar
Dave Jones committed
2524
	if (copy_to_user(&lv_status_bydev_req.lv->u.lv_current_pe, &saved_ptr1, sizeof(void *)) != 0)
Linus Torvalds's avatar
Linus Torvalds committed
2525
	        return -EFAULT;
Linus Torvalds's avatar
Linus Torvalds committed
2526 2527 2528 2529 2530 2531 2532 2533

	return 0;
} /* lvm_do_lv_status_bydev() */


/*
 * character device support function rename a logical volume
 */
Dave Jones's avatar
Dave Jones committed
2534
static int lvm_do_lv_rename(vg_t *vg_ptr, lv_req_t *lv_req, userlv_t *ulv)
Linus Torvalds's avatar
Linus Torvalds committed
2535 2536 2537 2538 2539 2540 2541 2542
{
	int l = 0;
	int ret = 0;
	lv_t *lv_ptr = NULL;

	for (l = 0; l < vg_ptr->lv_max; l++)
	{
		if ( (lv_ptr = vg_ptr->lv[l]) == NULL) continue;
Dave Jones's avatar
Dave Jones committed
2543
		if (kdev_same(lv_ptr->u.lv_dev, ulv->lv_dev))
Linus Torvalds's avatar
Linus Torvalds committed
2544
		{
Linus Torvalds's avatar
Linus Torvalds committed
2545
			lvm_fs_remove_lv(vg_ptr, lv_ptr);
Dave Jones's avatar
Dave Jones committed
2546
			strncpy(lv_ptr->u.lv_name,
Linus Torvalds's avatar
Linus Torvalds committed
2547 2548
				lv_req->lv_name,
				NAME_LEN);
Linus Torvalds's avatar
Linus Torvalds committed
2549
			lvm_fs_create_lv(vg_ptr, lv_ptr);
Linus Torvalds's avatar
Linus Torvalds committed
2550 2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565
			break;
		}
	}
	if (l == vg_ptr->lv_max) ret = -ENODEV;

	return ret;
} /* lvm_do_lv_rename */


/*
 * character device support function physical volume change
 */
static int lvm_do_pv_change(vg_t *vg_ptr, void *arg)
{
	uint p;
	pv_t *pv_ptr;
Linus Torvalds's avatar
Linus Torvalds committed
2566
	struct block_device *bd;
Linus Torvalds's avatar
Linus Torvalds committed
2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577

	if (vg_ptr == NULL) return -ENXIO;
	if (copy_from_user(&pv_change_req, arg,
			   sizeof(pv_change_req)) != 0)
		return -EFAULT;

	for (p = 0; p < vg_ptr->pv_max; p++) {
		pv_ptr = vg_ptr->pv[p];
		if (pv_ptr != NULL &&
		    strcmp(pv_ptr->pv_name,
			       pv_change_req.pv_name) == 0) {
Linus Torvalds's avatar
Linus Torvalds committed
2578 2579

			bd = pv_ptr->bd;
Linus Torvalds's avatar
Linus Torvalds committed
2580 2581 2582 2583
			if (copy_from_user(pv_ptr,
					   pv_change_req.pv,
					   sizeof(pv_t)) != 0)
				return -EFAULT;
Linus Torvalds's avatar
Linus Torvalds committed
2584
			pv_ptr->bd = bd;
Linus Torvalds's avatar
Linus Torvalds committed
2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 2601 2602 2603 2604 2605 2606 2607 2608 2609 2610 2611 2612 2613 2614 2615 2616 2617 2618 2619 2620 2621 2622 2623

			/* We don't need the PE list
			   in kernel space as with LVs pe_t list */
			pv_ptr->pe = NULL;
			return 0;
		}
	}
	return -ENXIO;
} /* lvm_do_pv_change() */

/*
 * character device support function get physical volume status
 */
static int lvm_do_pv_status(vg_t *vg_ptr, void *arg)
{
	uint p;
	pv_t *pv_ptr;

	if (vg_ptr == NULL) return -ENXIO;
	if (copy_from_user(&pv_status_req, arg,
			   sizeof(pv_status_req)) != 0)
		return -EFAULT;

	for (p = 0; p < vg_ptr->pv_max; p++) {
		pv_ptr = vg_ptr->pv[p];
		if (pv_ptr != NULL &&
		    strcmp(pv_ptr->pv_name,
			       pv_status_req.pv_name) == 0) {
			if (copy_to_user(pv_status_req.pv,
					 pv_ptr,
				         sizeof(pv_t)) != 0)
				return -EFAULT;
			return 0;
		}
	}
	return -ENXIO;
} /* lvm_do_pv_status() */

/*
Linus Torvalds's avatar
Linus Torvalds committed
2624
 * character device support function flush and invalidate all buffers of a PV
Linus Torvalds's avatar
Linus Torvalds committed
2625
 */
Linus Torvalds's avatar
Linus Torvalds committed
2626 2627 2628
static int lvm_do_pv_flush(void *arg)
{
       pv_flush_req_t pv_flush_req;
Linus Torvalds's avatar
Linus Torvalds committed
2629

Linus Torvalds's avatar
Linus Torvalds committed
2630 2631 2632
       if (copy_from_user(&pv_flush_req, arg,
                          sizeof(pv_flush_req)) != 0)
               return -EFAULT;
Linus Torvalds's avatar
Linus Torvalds committed
2633

Linus Torvalds's avatar
Linus Torvalds committed
2634 2635
       fsync_dev(pv_flush_req.pv_dev);
       invalidate_buffers(pv_flush_req.pv_dev);
Linus Torvalds's avatar
Linus Torvalds committed
2636

Linus Torvalds's avatar
Linus Torvalds committed
2637
       return 0;
Linus Torvalds's avatar
Linus Torvalds committed
2638 2639 2640 2641 2642
}

/*
 * support function initialize gendisk variables
 */
Linus Torvalds's avatar
Linus Torvalds committed
2643
static void __init lvm_geninit(struct gendisk *lvm_gdisk)
Linus Torvalds's avatar
Linus Torvalds committed
2644 2645 2646 2647 2648 2649 2650 2651 2652 2653 2654 2655 2656 2657 2658 2659 2660 2661 2662 2663
{
	int i = 0;

#ifdef DEBUG_GENDISK
	printk(KERN_DEBUG "%s -- lvm_gendisk\n", lvm_name);
#endif

	for (i = 0; i < MAX_LV; i++) {
		lvm_gendisk.part[i].start_sect = -1;	/* avoid partition check */
		lvm_size[i] = lvm_gendisk.part[i].nr_sects = 0;
		lvm_blocksizes[i] = BLOCK_SIZE;
	}

	blk_size[MAJOR_NR] = lvm_size;
	blksize_size[MAJOR_NR] = lvm_blocksizes;

	return;
} /* lvm_gen_init() */


Linus Torvalds's avatar
Linus Torvalds committed
2664 2665

/* Must have down_write(_pe_lock) when we enqueue buffers */
Linus Torvalds's avatar
Linus Torvalds committed
2666 2667 2668
static void _queue_io(struct bio *bh, int rw) {
	if (bh->bi_next) BUG();
	bh->bi_next = _pe_requests;
Linus Torvalds's avatar
Linus Torvalds committed
2669 2670 2671 2672
	_pe_requests = bh;
}

/* Must have down_write(_pe_lock) when we dequeue buffers */
Linus Torvalds's avatar
Linus Torvalds committed
2673
static struct bio *_dequeue_io(void)
Linus Torvalds's avatar
Linus Torvalds committed
2674
{
Linus Torvalds's avatar
Linus Torvalds committed
2675
	struct bio *bh = _pe_requests;
Linus Torvalds's avatar
Linus Torvalds committed
2676 2677 2678 2679 2680 2681 2682 2683 2684 2685 2686 2687 2688 2689
	_pe_requests = NULL;
	return bh;
}

/*
 * We do not need to hold _pe_lock to flush buffers.  bh should be taken from
 * _pe_requests under down_write(_pe_lock), and then _pe_requests can be set
 * NULL and we drop _pe_lock.  Any new buffers defered at this time will be
 * added to a new list, and the old buffers can have their I/O restarted
 * asynchronously.
 *
 * If, for some reason, the same PE is locked again before all of these writes
 * have finished, then these buffers will just be re-queued (i.e. no danger).
 */
Linus Torvalds's avatar
Linus Torvalds committed
2690
static void _flush_io(struct bio *bh)
Linus Torvalds's avatar
Linus Torvalds committed
2691 2692
{
	while (bh) {
Linus Torvalds's avatar
Linus Torvalds committed
2693 2694
		struct bio *next = bh->bi_next;
		bh->bi_next = NULL;
Linus Torvalds's avatar
Linus Torvalds committed
2695
		/* resubmit this buffer head */
Linus Torvalds's avatar
Linus Torvalds committed
2696 2697
		bh->bi_rw = WRITE; /* needed? */
		generic_make_request(bh);
Linus Torvalds's avatar
Linus Torvalds committed
2698 2699 2700 2701
		bh = next;
	}
}

Linus Torvalds's avatar
Linus Torvalds committed
2702
/*
Linus Torvalds's avatar
Linus Torvalds committed
2703
 * we must open the pv's before we use them
Linus Torvalds's avatar
Linus Torvalds committed
2704
 */
Linus Torvalds's avatar
Linus Torvalds committed
2705 2706 2707
static int _open_pv(pv_t *pv) {
	int err;
	struct block_device *bd;
Linus Torvalds's avatar
Linus Torvalds committed
2708

Linus Torvalds's avatar
Linus Torvalds committed
2709 2710 2711 2712 2713 2714
	if (!(bd = bdget(kdev_t_to_nr(pv->pv_dev))))
		return -ENOMEM;

	err = blkdev_get(bd, FMODE_READ|FMODE_WRITE, 0, BDEV_FILE);
	if (err)
		return err;
Linus Torvalds's avatar
Linus Torvalds committed
2715

Linus Torvalds's avatar
Linus Torvalds committed
2716 2717 2718
	pv->bd = bd;
	return 0;
}
Linus Torvalds's avatar
Linus Torvalds committed
2719

Linus Torvalds's avatar
Linus Torvalds committed
2720 2721 2722 2723 2724 2725
static void _close_pv(pv_t *pv) {
	if (pv) {
		struct block_device *bdev = pv->bd;
		pv->bd = NULL;
		if (bdev)
			blkdev_put(bdev, BDEV_FILE);
Linus Torvalds's avatar
Linus Torvalds committed
2726
	}
Linus Torvalds's avatar
Linus Torvalds committed
2727
}
Linus Torvalds's avatar
Linus Torvalds committed
2728

Linus Torvalds's avatar
Linus Torvalds committed
2729 2730 2731 2732 2733
static unsigned long _sectors_to_k(unsigned long sect)
{
	if(SECTOR_SIZE > 1024) {
		return sect * (SECTOR_SIZE / 1024);
	}
Linus Torvalds's avatar
Linus Torvalds committed
2734

Linus Torvalds's avatar
Linus Torvalds committed
2735
	return sect / (1024 / SECTOR_SIZE);
Linus Torvalds's avatar
Linus Torvalds committed
2736
}
Linus Torvalds's avatar
Linus Torvalds committed
2737 2738 2739

module_init(lvm_init);
module_exit(lvm_cleanup);
Linus Torvalds's avatar
Linus Torvalds committed
2740
MODULE_LICENSE("GPL");