All of lore.kernel.org
 help / color / mirror / Atom feed
From: Kevin Corry <corryk@us.ibm.com>
To: torvalds@transmeta.com
Cc: linux-kernel@vger.kernel.org, evms-devel@lists.sourceforge.net
Subject: [PATCH] EVMS core (1/9) evms.c
Date: Thu, 10 Oct 2002 14:35:03 -0500	[thread overview]
Message-ID: <02101014350303.17770@boiler> (raw)
In-Reply-To: <02101014305502.17770@boiler>

Greetings,

Part 1 of the EVMS core driver.

This file provides all of the block device requirements, the module
init/exit routines, and the ioctl interface to EVMS.

Kevin Corry
corryk@us.ibm.com
http://evms.sourceforge.net/



diff -Naur linux-2.5.41/drivers/evms/core/evms.c linux-2.5.41-evms/drivers/evms/core/evms.c
--- linux-2.5.41/drivers/evms/core/evms.c Sun Jul 17 18:46:18 1994
+++ linux-2.5.41-evms/drivers/evms/core/evms.c	Thu Oct 10 13:50:01 2002
@@ -0,0 +1,2692 @@
+/*
+ *   Copyright (c) International Business Machines  Corp., 2000 - 2002
+ *
+ *   This program 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 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY;  without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
+ *   the GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program;  if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+/*
+ * EVMS core driver.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/blk.h>
+#include <linux/blkpg.h>
+#include <linux/bio.h>
+#include <linux/hdreg.h>
+#include <linux/mempool.h>
+#include <linux/reboot.h>
+#include <asm/uaccess.h>
+#include <asm/checksum.h>
+
+#include <linux/evms.h>
+#include <linux/evms_ioctl.h>
+#include "evms_core.h"
+
+/**
+ * Global data
+ **/
+/*
+ * evms_info_level is the in-memory syslog level
+ */
+int evms_info_level = EVMS_INFO_LEVEL;
+EXPORT_SYMBOL(evms_info_level);
+struct proc_dir_entry *evms_proc_dir = NULL;
+
+/*
+ * the rediscover semaphore
+ */
+static struct semaphore red_sem;
+
+/*
+ * list of all registered (loaded) plugins
+ */
+struct list_head plugin_head;
+spinlock_t plugin_lock = SPIN_LOCK_UNLOCKED;
+
+/*
+ * list of all disk devices EVMS is aware of
+ */
+struct list_head evms_device_list;
+EXPORT_SYMBOL(evms_device_list);
+
+/*
+ * list of all bottom level storage objects with EVMS metadata
+ */
+struct list_head evms_fbottom_list;
+
+/*
+ * list of all registered notification events
+ */
+struct list_head evms_notify_list;
+
+struct list_head evms_logical_volumes;
+int evms_volumes = 0;
+
+/* 
+ * internal POOL variables 
+ */
+mempool_t *evms_io_notify_pool;
+static kmem_cache_t *evms_io_notify_slab;
+
+/* 
+ * Handle for the devfs directory entry 
+ */
+devfs_handle_t evms_dir_devfs_handle;
+devfs_handle_t evms_blk_devfs_handle;
+
+/**
+ * SYSCTL - EVMS folder definitions/variables
+ **/
+static struct ctl_table_header *evms_table_header;
+static int evms_info_level_min = EVMS_INFO_CRITICAL;
+static int evms_info_level_max = EVMS_INFO_EVERYTHING;
+
+static struct ctl_table evms_table[] = {
+	{DEV_EVMS_INFO_LEVEL, "evms_info_level",
+	 &evms_info_level, sizeof (int), 0644, NULL,
+	 &proc_dointvec_minmax, &sysctl_intvec,
+	 NULL, &evms_info_level_min, &evms_info_level_max},
+	{0}
+};
+
+static struct ctl_table evms_dir_table[] = {
+	{DEV_EVMS, "evms", NULL, 0, 0555, evms_table},
+	{0}
+};
+
+static struct ctl_table dev_dir_table[] = {
+	{CTL_DEV, "dev", NULL, 0, 0555, evms_dir_table},
+	{0}
+};
+
+/**
+ * find_next_volume - locates first or next logical volume
+ * @lv:		current logical volume
+ *
+ * returns the next logical volume or NULL
+ **/
+struct evms_logical_volume *
+find_next_volume(struct evms_logical_volume *lv)
+{
+	struct list_head *list_member =
+		(lv) ? &lv->volumes : &evms_logical_volumes;
+	for (lv = list_entry(list_member->next, typeof(*lv), volumes),
+		     prefetch(lv->volumes.next);
+	     &lv->volumes != &evms_logical_volumes; 
+	     lv = list_entry(lv->volumes.next, typeof(*lv), volumes),
+		     prefetch(lv->volumes.next)) {
+		return lv;
+	}
+	return NULL;
+}
+
+/**
+ * find_next_volume_safe - locates first or next logical volume (safe for removes)
+ * @next_lv:	ptr to next logical volume
+ *
+ * returns the next logical volume or NULL
+ **/
+struct evms_logical_volume *
+find_next_volume_safe(struct evms_logical_volume **next_lv)
+{
+	struct evms_logical_volume *lv =
+		(*next_lv) ? *next_lv :
+                list_entry(evms_logical_volumes.next, typeof(*lv), volumes);
+	*next_lv = list_entry(lv->volumes.next, typeof(*lv), volumes);
+	if (&lv->volumes != &evms_logical_volumes) {
+		return lv;
+	}
+	return NULL;
+}
+
+/**
+ * lookup_volume - finds a logical volume by minor number
+ * @minor:	minor number of logical volume to be found
+ *
+ * returns the logical volume of the specified minor or NULL.
+ **/
+struct evms_logical_volume *
+lookup_volume(int minor)
+{
+	struct evms_logical_volume *lv;
+	list_for_each_entry(lv, &evms_logical_volumes, volumes) {
+		if (lv->minor == minor) {
+			return lv;
+		}
+	}
+	return NULL;
+}
+
+/**
+ * remove_logical_volume - removes a logical volume from our list
+ * @lv:		logical volume to be removed.
+ *
+ * removes an existing logical volume from our list.
+ **/
+static void
+remove_logical_volume(struct evms_logical_volume *lv)
+{
+	BUG_ON(list_empty(&lv->volumes));
+	list_del_init(&lv->volumes);
+}
+
+/**********************************************************/
+/* START -- Proc FS Support functions                     */
+/**********************************************************/
+
+/**
+ * evms_info_read_proc - /proc/evms/info support function
+ * @page:	procfs required field
+ * @start:	procfs required field
+ * @off:	procfs required field
+ * @count:	procfs required field
+ * @eof:	procfs required field
+ * @data:	procfs required field
+ *
+ * cat /proc/evms/info function that provides info about EVMS.
+ **/
+static int
+evms_info_read_proc(char *page, char **start, off_t off,
+		    int count, int *eof, void *data)
+{
+	int sz = 0;
+	char *info_level_text = NULL;
+
+	PROCPRINT("Enterprise Volume Management System: Info\n");
+	switch (evms_info_level) {
+	case EVMS_INFO_CRITICAL:
+		info_level_text = "critical";
+		break;
+	case EVMS_INFO_SERIOUS:
+		info_level_text = "serious";
+		break;
+	case EVMS_INFO_ERROR:
+		info_level_text = "error";
+		break;
+	case EVMS_INFO_WARNING:
+		info_level_text = "warning";
+		break;
+	case EVMS_INFO_DEFAULT:
+		info_level_text = "default";
+		break;
+	case EVMS_INFO_DETAILS:
+		info_level_text = "details";
+		break;
+	case EVMS_INFO_DEBUG:
+		info_level_text = "debug";
+		break;
+	case EVMS_INFO_EXTRA:
+		info_level_text = "extra";
+		break;
+	case EVMS_INFO_ENTRY_EXIT:
+		info_level_text = "entry exit";
+		break;
+	case EVMS_INFO_EVERYTHING:
+		info_level_text = "everything";
+		break;
+	default:
+		info_level_text = "unknown";
+		break;
+	}
+	PROCPRINT("EVMS info level: %d (%s).\n",
+		  evms_info_level, info_level_text);
+
+	PROCPRINT("EVMS kernel version: %d.%d.%d\n",
+		  EVMS_MAJOR_VERSION,
+		  EVMS_MINOR_VERSION, EVMS_PATCHLEVEL_VERSION);
+
+	PROCPRINT("EVMS IOCTL interface version: %d.%d.%d\n",
+		  EVMS_IOCTL_INTERFACE_MAJOR,
+		  EVMS_IOCTL_INTERFACE_MINOR, EVMS_IOCTL_INTERFACE_PATCHLEVEL);
+
+	PROCPRINT("EVMS Common Services version: %d.%d.%d\n",
+		  EVMS_COMMON_SERVICES_MAJOR,
+		  EVMS_COMMON_SERVICES_MINOR, EVMS_COMMON_SERVICES_PATCHLEVEL);
+
+	*eof = 1;
+
+out:
+	*start = page + off;
+	sz -= off;
+	if (sz < 0)
+		sz = 0;
+	return sz > count ? count : sz;
+}
+
+/**
+ * evms_plugins_read_proc - /proc/evms/plugins support function
+ * @page:	procfs required field
+ * @start:	procfs required field
+ * @off:	procfs required field
+ * @count:	procfs required field
+ * @eof:	procfs required field
+ * @data:	procfs required field
+ *
+ * cat /proc/evms/plugins that lists the currently loaded plugin modules in EVMS.
+ **/
+static int
+evms_plugins_read_proc(char *page,
+		       char **start, off_t off, int count, int *eof, void *data)
+{
+	int sz = 0;
+	struct evms_plugin_header *plugin;
+
+	PROCPRINT("Enterprise Volume Management System: Plugins\n");
+	/*             0    1    1    2    2    3    3    4    4    5    5    6    6    7 */
+	/*         1   5    0    5    0    5    0    5    0    5    0    5    0    5    0 */
+	PROCPRINT(" ---------Plugin----------\n");
+	PROCPRINT(" ----id----        version\n\n");
+	spin_lock(&plugin_lock);
+	list_for_each_entry(plugin, &plugin_head, headers) {
+		PROCPRINT(" %x.%x.%x\t   %d.%d.%d\n",
+			  GetPluginOEM(plugin->id),
+			  GetPluginType(plugin->id),
+			  GetPluginID(plugin->id),
+			  plugin->version.major,
+			  plugin->version.minor,
+			  plugin->version.patchlevel);
+	}
+	spin_unlock(&plugin_lock);
+
+out:
+	*start = page + off;
+	sz -= off;
+	if (sz < 0)
+		sz = 0;
+	return sz > count ? count : sz;
+}
+
+/**
+ * evms_volumes_read_proc - /proc/evms/volumes support function
+ * @page:	procfs required field
+ * @start:	procfs required field
+ * @off:	procfs required field
+ * @count:	procfs required field
+ * @eof:	procfs required field
+ * @data:	procfs required field
+ *
+ * cat /proc/evms/volumes that lists the currently exported volumes in EVMS.
+ **/
+static int
+evms_volumes_read_proc(char *page,
+		       char **start, off_t off, int count, int *eof, void *data)
+{
+	struct evms_logical_volume *lv = NULL;
+	int sz = 0;
+
+	PROCPRINT("Enterprise Volume Management System: Volumes\n");
+	PROCPRINT("major   minor          #blocks type   flags name\n\n");
+	while ((lv = find_next_volume(lv))) {
+		if (!lv->node) {
+			continue;
+		}
+		PROCPRINT("%5d %7d %16Ld %s %s %s %s%s\n",
+			  EVMS_MAJOR, lv->minor,
+			  (long long)lv->node->total_vsectors >> 1,
+			  (lv->flags & EVMS_VOLUME_FLAG) ? 
+			  "evms  " : "compat",
+			  (lv->flags & EVMS_VOLUME_READ_ONLY) ? 
+			  "ro" : "rw",
+			  (lv->flags & EVMS_VOLUME_PARTIAL) ? 
+			  "p " : "  ",
+			  EVMS_DIR_NAME "/", lv->name);
+	}
+out:
+	*start = page + off;
+	sz -= off;
+	if (sz < 0)
+		sz = 0;
+	return sz > count ? count : sz;
+}
+
+/**********************************************************/
+/* END -- Proc FS Support functions                       */
+/**********************************************************/
+
+/**********************************************************/
+/* START -- FOPS functions definitions                    */
+/**********************************************************/
+
+/**
+ * is_busy - determines if a block_devices is currently in use
+ * @dev:	device to check
+ *
+ * determines if a block_device is in use or not
+ *
+ * returns: 0 = device is not in use
+ *	    -EBUSY if device is in use
+ *	    -ENOMEM if unable to get a bdev
+ **/
+int
+is_busy(kdev_t dev)
+{
+	struct block_device *bdev;
+
+	bdev = bdget(kdev_t_to_nr(dev));
+	if (!bdev)
+		return -ENOMEM;
+	if (bd_claim(bdev, is_busy))
+		return -EBUSY;
+	bd_release(bdev);
+	return 0;
+}
+
+/************************************************/
+/* START -- IOCTL commands -- EVMS specific     */
+/************************************************/
+
+/**
+ * evms_ioctl_cmd_get_ioctl_version
+ * @arg:	evms version packet
+ *
+ * retrieves the evms ioctl interface version
+ *
+ * returns: 0 = success
+ *	    otherwise error code
+ **/
+static int
+evms_ioctl_cmd_get_ioctl_version(void *arg)
+{
+	struct evms_version ver;
+
+	ver.major = EVMS_IOCTL_INTERFACE_MAJOR;
+	ver.minor = EVMS_IOCTL_INTERFACE_MINOR;
+	ver.patchlevel = EVMS_IOCTL_INTERFACE_PATCHLEVEL;
+
+	/* copy info to userspace */
+	if (copy_to_user(arg, &ver, sizeof (ver)))
+		return -EFAULT;
+
+	return 0;
+}
+
+/**
+ * evms_ioctl_cmd_get_version
+ * @arg:	evms version packet
+ *
+ * retrieves the evms kernel version
+ *
+ * returns: 0 = success
+ *	    otherwise error code
+ **/
+static int
+evms_ioctl_cmd_get_version(void *arg)
+{
+	struct evms_version ver;
+
+	ver.major = EVMS_MAJOR_VERSION;
+	ver.minor = EVMS_MINOR_VERSION;
+	ver.patchlevel = EVMS_PATCHLEVEL_VERSION;
+
+	/* copy info to userspace */
+	if (copy_to_user(arg, &ver, sizeof (ver)))
+		return -EFAULT;
+
+	return 0;
+}
+
+/**
+ * evms_ioctl_cmd_get_info_level
+ * @arg:	int value
+ *
+ * retrieves the evms info (syslog logging) level
+ *
+ * returns: 0 = success
+ *	    otherwise error code
+ **/
+static int
+evms_ioctl_cmd_get_info_level(void *arg)
+{
+	/* copy info to userspace */
+	if (copy_to_user(arg, &evms_info_level, sizeof (evms_info_level)))
+		return -EFAULT;
+
+	return 0;
+}
+
+/**
+ * evms_ioctl_cmd_set_info_level
+ * @arg:	int value
+ *
+ * sets the evms info (syslog logging) level
+ *
+ * returns: 0 = success
+ *	    otherwise error code
+ **/
+static int
+evms_ioctl_cmd_set_info_level(void *arg)
+{
+	int temp;
+
+	/* copy info from userspace */
+	if (copy_from_user(&temp, arg, sizeof (temp)))
+		return -EFAULT;
+	evms_info_level = temp;
+
+	return 0;
+}
+
+/**
+ * evms_quiesce_volume
+ * @volume:	volume to be quiesced
+ * @command:	0 = unquiesce, 1 = quiesce
+ * @minor:	minor of volume to be quiesce
+ * @lock_vfs:	0 = no lock, 1 = lock
+ *
+ * this function performs the actual quiesce operation on
+ * a volume in kernel memory.
+ *
+ * when quiescing, all new I/Os to a volume are stopped,
+ * causing the calling thread to block. this thread then
+ * waits until all I/Os in progress are completed, before
+ * return control to the caller.
+ *
+ * when unquiescing, all new I/Os are allowed to proceed
+ * unencumbered, and all threads waiting (blocked) on this
+ * volume, are woken up and allowed to proceed.
+ *
+ * returns: 0 = success
+ *	    otherwise error code
+ */
+int
+evms_quiesce_volume(struct evms_logical_volume *volume,
+		    int command, int minor, int lock_vfs)
+{
+	int rc;
+
+	LOG_DEBUG("%squiescing %s.\n",
+		  ((command) ? "" : "un"), volume->name);
+
+	if (lock_vfs) {
+		/* VFS function call to sync and lock the filesystem */
+		fsync_dev_lockfs(mk_kdev(EVMS_MAJOR, minor));
+		volume->vfs_quiesced = 1;
+	}
+
+	volume->quiesced = command;
+
+	/* Command specified was "quiesce". */
+	if (command) {
+		/* After setting the volume to a quiesced state, there could
+		 * still be I/O requests in progress. Wait for the request
+		 * count to go to zero before continuing.
+		 */
+		wait_event(volume->quiesce_wait_queue,
+			   (atomic_read(&volume->requests_in_progress) == 0));
+	}
+
+	/* Send this command down the stack so lower */
+	/* layers can know about this                */
+	rc = QUIESCE(volume->node, command);
+	
+	/* Command specified was "unquiesce". */
+	if (!command) {
+		/* "wakeup" any I/O requests waiting on this volume */
+		wake_up(&volume->request_wait_queue);
+		if (volume->vfs_quiesced) {
+			/* VFS function call to unlock the filesystem */
+			unlockfs(mk_kdev(EVMS_MAJOR, minor));
+			volume->vfs_quiesced = 0;
+		}
+	}
+	if (rc) {
+		LOG_ERROR("error(%d) %squiescing %s.\n",
+			  rc, ((command) ? "" : "un"), volume->name);
+	}
+	return rc;
+}
+
+/**
+ * evms_delete_volume
+ * @volume:	logical volume being deleted
+ * @command:	0 = "soft", 1 = "hard" delete request
+ * @minor:	minor of volume to be deleted
+ * @associative_minor: minor of volume associated to the @minor volume
+ *
+ * this function performs the actual delete operation on
+ * a volume to purge it from kernel memory. all structures
+ * and memory consumed by this volume will be free as well
+ * as clearing or unregistering any system services.
+ *
+ * returns: 0 = on success
+ *          -EBUSY if volume is mounted
+ *	    otherwise error code
+ **/
+static int
+evms_delete_volume(struct evms_logical_volume *lv,
+		   int command, int minor, int associative_minor)
+{
+	int rc = 0;
+	struct block_device *bdev = NULL;
+
+	/* if this is a "permament" delete */
+	/* check to make sure volume is not in use */
+	if (command) {
+		bdev = bdget(kdev_t_to_nr(mk_kdev(EVMS_MAJOR, lv->minor)));
+		if (!bdev) {
+			if (command == 1) {
+				return -ENOMEM;
+			}
+			command = 0;
+		} 
+	}
+	if (command) {
+		if (bd_claim(bdev, evms_delete_volume)) {
+			if (command == 1) {
+				return -EBUSY;
+			}
+			command = 0;
+		}
+	}
+       	if (command) {
+		/* invalidate the device since it is not coming back
+		 * this is required incase we are re-using the minor number
+		 */
+		invalidate_device(mk_kdev(EVMS_MAJOR, minor), 1);
+	}
+	/* invoke the delete ioctl at the top of the feature stack */
+	LOG_DETAILS("deleting '%s'.\n", lv->name);
+	rc = DELETE(lv->node);
+	if (rc) {
+		LOG_ERROR("error(%d) %s deleting %s.\n",
+			  rc, ((command) ? "hard" : "soft"), lv->name);
+		return rc;
+	}
+	/* the volume has been deleted, do any clean up work
+	 * required.
+	 */
+	devfs_unregister(lv->gd->de);
+	set_device_ro(mk_kdev(EVMS_MAJOR, minor), 0);
+	del_gendisk(lv->gd);
+	put_disk(lv->gd);
+	lv->gd = NULL;
+	lv->node = NULL;
+	evms_volumes--;
+	if (command) {
+		/* if "permanent" delete, free the name
+		 * and NULL the name field.
+		 */
+		blk_cleanup_queue(&lv->request_queue);
+		remove_logical_volume(lv);
+		kfree(lv->name);
+		bd_release(bdev);
+		kfree(lv);
+	} else {
+		/* if "soft" delete, leave the name so
+		 * we can use it to reassign the same
+		 * minor to this volume after a
+		 * rediscovery.
+		 */
+		lv->flags = EVMS_VOLUME_SOFT_DELETED;
+	}
+	return 0;
+}
+
+/**
+ * evms_user_delete_volume
+ * @lvt:	logical volume
+ * @inode:	vfs ioctl parameter
+ * @file:	vfs ioctl parameter
+ * @dv:		delete volume ioctl packet
+ *
+ * this function, depending on the parameters, performs
+ * a "soft" or a "hard" delete. for a "soft" delete, a
+ * quiesce & delete request is queued up, to be executed
+ * at the beginning of the next rediscovery. for a
+ * "hard" delete, the target volume is quiesced and then
+ * deleted. if there is any errors attempting to delete
+ * the target, then the target is unquiesced. if an
+ * associative volume is specified it is quiesced before
+ * the target volume is quiesced, and is unquiesced
+ * after the attempt to delete the target volume.
+ **/
+static int
+evms_user_delete_volume(struct evms_logical_volume *lvt,
+			struct inode *inode, struct file *file, 
+			struct evms_delete_vol_pkt *dv)
+{
+	int rc, qa = 0;
+	struct evms_logical_volume *lva = NULL;
+
+	if (!dv->command) {
+		/* "soft delete" requested */
+		lvt->flags |= (EVMS_REQUESTED_QUIESCE | EVMS_REQUESTED_DELETE);
+		if (dv->do_vfs) {
+			lvt->flags |= EVMS_REQUESTED_VFS_QUIESCE;
+		}
+		return 0;
+	}
+	/* "hard delete" requested */
+	if (dv->associative_minor) {
+		/* associative volume specified
+		 *
+		 * quiesce it
+		 */
+		lva = lookup_volume(dv->associative_minor);
+		/* quiesce associative volume */
+		rc = evms_quiesce_volume(lva, 1,
+					 dv->associative_minor,
+					 0);
+		if (!rc) {
+			qa = 1;
+		} else {
+			goto error;
+		}
+	}
+	/* quiesce target volume */
+	rc = evms_quiesce_volume(lvt, 1, dv->minor, 0);
+	if (rc) {
+		goto error;
+	}
+	/* delete the target volume */
+	rc = evms_delete_volume(lvt, dv->command, dv->minor,
+				dv->associative_minor);
+	if (rc) {
+		/* got an error undeleting...
+		 *
+		 * unquiesce the target
+		 */
+		rc = evms_quiesce_volume(lvt, 0, dv->minor, 0);
+	}
+      error:
+	if (dv->associative_minor) {
+		/* associative volume specified
+		 *
+		 * unquiesce it
+		 */
+		if (qa) {
+			/* only unquiesce associative
+			 * if we successfully quiesced
+			 * it previously.
+			 */
+			rc = evms_quiesce_volume(lva, 0,
+						 dv->associative_minor,
+						 0);
+		}
+	}
+	return rc;
+}
+
+/**
+ * evms_ioctl_cmd_delete_volume
+ * @inode:	vfs ioctl parameter
+ * @file:	vfs ioctl parameter
+ * @arg:	delete volume ioctl packet
+ *
+ * this function will copy user data to/from the kernel, and
+ * validates user parameters. after validation, control
+ * is passed to worker routine evms_user_delete_volume.
+ *
+ * returns: 0 on success
+ *	    otherwise error code
+ **/
+static int
+evms_ioctl_cmd_delete_volume(struct inode *inode, struct file *file, ulong arg)
+{
+	int rc = 0;
+	struct evms_delete_vol_pkt tmp, *user_parms;
+	struct evms_logical_volume *lv = NULL;
+
+	user_parms = (struct evms_delete_vol_pkt *) arg;
+	/* copy user's parameters to kernel space */
+	if (copy_from_user(&tmp, user_parms, sizeof (tmp))) {
+		rc = -EFAULT;
+		goto exit;
+	}
+	/* check to make sure associative minor is in use */
+	if (tmp.associative_minor) {
+		lv = lookup_volume(tmp.associative_minor);
+		if (lv == NULL || lv->node == NULL) {
+			rc = -ENXIO;
+			goto exit;
+		}
+	}
+	/* check to make sure target minor is in use */
+	lv = lookup_volume(tmp.minor);
+	if (lv == NULL || lv->node == NULL) {
+		rc = -ENXIO;
+	} else {
+		rc = evms_user_delete_volume(lv, inode, file, &tmp);
+	}
+      exit:
+	/* copy the status value back to the user */
+	tmp.status = rc;
+	if (copy_to_user(user_parms, &tmp, sizeof (tmp))) {
+		rc = -EFAULT;
+	}
+	return rc;
+}
+
+/**
+ * evms_full_rediscover_prep
+ * @inode:	vfs ioctl parameter
+ * @file:	vfs ioctl parameter
+ *
+ * this function helps to prevent problems when evms is
+ * configured with the base built in statically and some
+ * plugins built as modules.
+ *
+ * in these cases, when the initial discovery is done,
+ * only the statically built modules are available for
+ * volume construction. as a result, some volumes that
+ * require the plugins built as modules (which haven't
+ * been loaded) to be fully reconstructed, may come up
+ * as compatibility volumes or partial volumes.
+ *
+ * when parts of evms are built as modules, the
+ * evms_rediscovery utility is used, to perform a secondary
+ * rediscover, after all the plugins built as modules
+ * have been loaded, to construct all the volumes
+ * requiring these plugins.
+ *
+ * however since some of the volumes, requiring the plugins
+ * built as modules, may have been already exported as
+ * compatibility or partial volumes, we need to purge these
+ * volumes from kernel's memory, so they can be rediscovered
+ * and claimed by the appropriate plugins, and reconstructed
+ * into the correct volumes.
+ *
+ * this function purges all compatibility volumes that are
+ * not in use (unmounted) and all partial volumes, prior to
+ * doing the secondary rediscover, thus allowing volumes to
+ * be rediscovered correctly.
+ *
+ * NOTE: again, this is only required in cases when a
+ * combination of plugins are built statically and as
+ * modules.
+ **/
+static void
+evms_full_rediscover_prep(struct inode *inode, struct file *file)
+{
+	struct evms_logical_volume *lv, *next_lv = NULL;
+
+	LOG_DETAILS("%s: started.\n", __FUNCTION__);
+	/* check for acceptable volumes to be deleted */
+	while ((lv = find_next_volume_safe(&next_lv))) {
+		struct evms_delete_vol_pkt dv;
+		int volume_mounted, doit;
+		kdev_t devp;
+
+		if (!lv->node)
+			continue;
+		devp = mk_kdev(EVMS_MAJOR, lv->minor);
+		volume_mounted = (is_busy(devp)) ? 1 : 0;
+		/* only proceed on volumes that are:
+		 *   partial volumes
+		 *      OR
+		 *   unmounted compatibility volumes
+		 */
+		doit = 0;
+		if (lv->flags & EVMS_VOLUME_PARTIAL) {
+			/* do all partial volumes
+			 */
+			doit = 1;
+		} else if (!(lv->flags & EVMS_VOLUME_FLAG)) {
+			/* check all compatibility volumes
+			 */
+			if (!volume_mounted) {
+				/* only do unmounted volumes
+				 */
+				doit = 1;
+			}
+		}
+		if (doit == 0) {
+			continue;
+		}
+		/* delete the volume from memory.
+		 * do a 'soft' delete if volume
+		 * is mounted, and 'hard' delete
+		 * if it is not.
+		 *
+		 * NOTE: the delete operation will
+		 * clear the bits in the flags field.
+		 */
+		dv.command = (volume_mounted) ? EVMS_SOFT_DELETE : 
+			EVMS_HARD_DELETE;
+		dv.minor = lv->minor;
+		dv.do_vfs = dv.associative_minor = 0;
+		evms_user_delete_volume(lv, inode, file, &dv);
+	}
+	LOG_DETAILS("%s: completed.\n", __FUNCTION__);
+}
+
+/**
+ * evms_ioctl_cmd_rediscover_volumes
+ * @inode:	vfs ioctl parameter
+ * @file:	vfs ioctl parameter
+ * @cmd:	vfs ioctl parameter, the ioctl command
+ * @arg:	vfs ioctl parameter, the rediscover ioctl packet
+ *
+ * performs a rediscovery (probing) off all specified devices and exports any
+ * newly found volumes.
+ **/
+static int
+evms_ioctl_cmd_rediscover_volumes(struct inode *inode,
+				  struct file *file,
+				  unsigned int cmd, ulong arg)
+{
+	int rc;
+	struct evms_rediscover_pkt tmp, *user_parms;
+	u64 *array_ptr = NULL;
+	ulong array_size = 0;
+	struct evms_logical_volume *lv = NULL, *next_lv;
+
+	/* grab the rediscover semaphore */
+	down(&red_sem);
+
+	rc = tmp.drive_count = 0;
+	user_parms = (struct evms_rediscover_pkt *) arg;
+	/* copy user's parameters to kernel space */
+	if (copy_from_user(&tmp, user_parms, sizeof (tmp))) {
+		rc = -EFAULT;
+		goto exit;
+	}
+
+	if (tmp.drive_count == REDISCOVER_ALL_DEVICES) {
+		evms_full_rediscover_prep(inode, file);
+	}
+	/* quiesce all queued volumes */
+	while ((lv = find_next_volume(lv))) {
+		if (!lv->node) {
+			continue;
+		}
+		if (!(lv->flags & EVMS_REQUESTED_QUIESCE)) {
+			continue;
+		}
+		rc = evms_quiesce_volume(lv, 1, lv->minor,
+			(lv->flags & EVMS_REQUESTED_VFS_QUIESCE) ?
+			1 : 0);
+	}
+	/* "soft" delete all queued volumes */
+	next_lv = NULL;
+	while ((lv = find_next_volume_safe(&next_lv))) {
+		if (!lv->node) {
+			continue;
+		}
+		if (!(lv->flags & EVMS_REQUESTED_DELETE)) {
+			continue;
+		}
+		rc = evms_delete_volume(lv, EVMS_SOFT_DELETE,
+					lv->minor, 0);
+	}
+
+	if (tmp.drive_count && (tmp.drive_count != REDISCOVER_ALL_DEVICES)) {
+		/* create space for userspace drive array */
+		array_size = sizeof (*tmp.drive_array) * tmp.drive_count;
+		array_ptr = tmp.drive_array;
+		tmp.drive_array = kmalloc(array_size, GFP_KERNEL);
+		if (!tmp.drive_array) {
+			rc = -ENOMEM;
+			goto exit;
+		}
+		/* copy rediscover drive array to kernel space */
+		if (copy_from_user(tmp.drive_array, array_ptr, array_size)) {
+			rc = -EFAULT;
+			goto exit;
+		}
+	}
+	/* perform the rediscovery operation */
+	rc = evms_discover_volumes(&tmp);
+
+	/* clean up after operation */
+	if (tmp.drive_count && (tmp.drive_count != REDISCOVER_ALL_DEVICES))
+		kfree(tmp.drive_array);
+      exit:
+	/* set return code and copy info to userspace */
+	tmp.status = rc;
+	if (copy_to_user(&user_parms->status, &tmp.status, sizeof (tmp.status))) {
+		rc = -EFAULT;
+	}
+
+	/* release the rediscover semaphore */
+	up(&red_sem);
+	return rc;
+}
+
+/**
+ * evms_ioctl_cmd_get_logical_disk
+ * @arg:	vfs ioctl parameter, the disk handle ioctl packet
+ *
+ * retrieves the 1st or next device (disk) handle to the caller
+ *
+ * returns: 0 on success
+ * 	    otherwise error code
+ **/
+static struct list_head *user_disk_ptr;
+static int
+evms_ioctl_cmd_get_logical_disk(void *arg)
+{
+	struct evms_user_disk_pkt tmp, *user_parms;
+
+	user_parms = (struct evms_user_disk_pkt *) arg;
+	/* copy user's parameters to kernel space */
+	if (copy_from_user
+	    (&tmp.command, &user_parms->command, sizeof (tmp.command))) {
+		return -EFAULT;
+	}
+	if (tmp.command == EVMS_FIRST_DISK)
+		user_disk_ptr = evms_device_list.next;
+	else			/* tmp.command == EVMS_NEXT_DISK */
+		user_disk_ptr = user_disk_ptr->next;
+
+	if (user_disk_ptr == &evms_device_list)
+		tmp.status = EVMS_DISK_INVALID;
+	else {
+		struct evms_logical_node *node =
+		    list_entry(user_disk_ptr, struct evms_logical_node, device);
+		tmp.status = EVMS_DISK_VALID;
+		tmp.disk_handle = NODE_TO_DEV_HANDLE(node);
+	}
+	/* copy info to userspace */
+	if (copy_to_user(user_parms, &tmp, sizeof (tmp))) {
+		return -EFAULT;
+	}
+	return 0;
+}
+
+/**
+ * evms_ioctl_cmd_get_logical_disk_info
+ * @arg:	vfs ioctl parameter, the disk info ioctl packet
+ *
+ * fills in info about the device specified by the disk handle
+ *
+ * returns: 0 on success
+ * 	    otherwise error code
+ **/
+static int
+evms_ioctl_cmd_get_logical_disk_info(void *arg)
+{
+	int rc;
+	struct evms_user_disk_info_pkt tmp, *user_parms;
+	struct evms_logical_node *disk_node, *mem_node;
+
+	user_parms = (struct evms_user_disk_info_pkt *) arg;
+	/* copy user's parameters to kernel space */
+	if (copy_from_user
+	    (&tmp.disk_handle, &user_parms->disk_handle,
+	     sizeof (tmp.disk_handle))) {
+		return -EFAULT;
+	}
+	/* check handle for validity */
+	rc = -EINVAL;
+	disk_node = DEV_HANDLE_TO_NODE(tmp.disk_handle);
+	list_for_each_entry(mem_node, &evms_device_list, device) {
+		if (mem_node == disk_node) {
+			rc = 0;
+			break;
+		}
+	}
+	if (rc) {
+		goto exit;
+	}
+	/* populate kernel copy of user's structure with appropriate info */
+	tmp.flags = disk_node->flags;
+	strcpy(tmp.disk_name, EVMS_DIR_NAME "/");
+	strcat(tmp.disk_name, disk_node->name);
+	rc = evms_cs_kernel_ioctl(disk_node, EVMS_UPDATE_DEVICE_INFO, 
+				  (ulong) NULL);
+	if (rc) {
+		goto exit;
+	}
+	tmp.total_sectors = disk_node->total_vsectors;
+	tmp.hardsect_size = disk_node->hardsector_size;
+	tmp.block_size = disk_node->block_size;
+	rc = GET_GEO(disk_node, &tmp.geo_cylinders, &tmp.geo_heads,
+		     &tmp.geo_sectors, NULL);
+      exit:
+	/* set return code and copy info to userspace */
+	tmp.status = rc;
+	if (copy_to_user(user_parms, &tmp, sizeof (tmp))) {
+		rc = -EFAULT;
+	}
+	return rc;
+}
+
+/**
+ * evms_ioctl_cmd_sector_io - performs sector based IO to a disk device
+ * @arg:	vfs ioctl parameter, the sector io ioctl packet
+ *
+ * performs 512 byte sector based io on a specified device. internally this
+ * routine will do I/O upto 64KB at a time, breaking larger requests up as
+ * needed.
+ *
+ * returns: 0 on success
+ * 	    otherwise error code
+ **/
+#define MAX_IO_SIZE 128
+static int
+evms_ioctl_cmd_sector_io(void *arg)
+{
+	int rc;
+	struct evms_sector_io_pkt tmp, *user_parms;
+	struct evms_logical_node *disk_node, *mem_node;
+	u8 *io_buffer = NULL, *user_buffer_ptr;
+	u64 io_sector_offset, io_remaining, io_bytes, io_size = MAX_IO_SIZE;
+
+	user_parms = (struct evms_sector_io_pkt *) arg;
+	/* copy user's parameters to kernel space */
+	if (copy_from_user(&tmp, user_parms, sizeof (tmp))) {
+		return -EFAULT;
+	}
+	/* check handle for validity */
+	rc = -EINVAL;
+	disk_node = DEV_HANDLE_TO_NODE(tmp.disk_handle);
+	list_for_each_entry(mem_node, &evms_device_list, device) {
+		if (mem_node == disk_node) {
+			rc = 0;
+			break;
+		}
+	}
+	if (rc) {
+		goto exit;
+	}
+	/* allocate a io buffer upto 64Kbytes in size */
+	if (tmp.sector_count < MAX_IO_SIZE) {
+		io_size = tmp.sector_count;
+	}
+	/* allocate buffer large enough to hold a single sector */
+	io_buffer = kmalloc(io_size << EVMS_VSECTOR_SIZE_SHIFT, GFP_KERNEL);
+	if (!io_buffer) {
+		rc = -ENOMEM;
+		goto exit;
+	}
+	/* perform io with specified disk */
+	io_remaining = tmp.sector_count;
+	io_sector_offset = 0;
+	user_buffer_ptr = tmp.buffer_address;
+	while (io_remaining) {
+		/* compute the io_size for this pass */
+		io_size = (io_remaining >= MAX_IO_SIZE) ?
+		    MAX_IO_SIZE : io_remaining;
+		io_bytes = io_size << EVMS_VSECTOR_SIZE_SHIFT;
+		/* for writes, copy a sector from user to kernel */
+		if (tmp.io_flag == EVMS_SECTOR_IO_WRITE) {
+			/* copy sector from user data buffer */
+			if (copy_from_user
+			    (io_buffer, user_buffer_ptr, io_bytes)) {
+				rc = -EFAULT;
+				goto exit;
+			}
+		}
+		/* perform IO */
+		rc = INIT_IO(disk_node, tmp.io_flag,
+			     io_sector_offset + tmp.starting_sector,
+			     io_size, io_buffer);
+		if (rc) {
+			goto exit;
+		}
+		if (tmp.io_flag != EVMS_SECTOR_IO_WRITE) {
+			/* copy sector to user data buffer */
+			if (copy_to_user(user_buffer_ptr, io_buffer, io_bytes)) {
+				rc = -EFAULT;
+				goto exit;
+			}
+		}
+		user_buffer_ptr += io_bytes;
+		tmp.buffer_address += io_bytes;
+		io_sector_offset += io_size;
+		io_remaining -= io_size;
+	}
+exit:
+	/* if the sector_buffer was allocated, free it */
+	if (io_buffer)
+		kfree(io_buffer);
+
+	/* copy the status value back to the user */
+	tmp.status = rc;
+	if (copy_to_user(user_parms, &tmp, sizeof (tmp)))
+		rc = -EFAULT;
+
+	return rc;
+}
+
+#undef MAX_IO_SIZE
+
+/**
+ * evms_ioctl_cmd_get_minor - retrieves the volume minor device number
+ * @arg:	vfs ioctl parameter, the get minor ioctl packet
+ *
+ * retrieves the minor device number for the 1st or the next volume exported by EVMS.
+ *
+ * NOTE: this routine will look for, and purge any volumes previous mounted as
+ * corrupt that are no longer mounted.
+ *
+ * returns: 0 on success
+ * 	    otherwise error code
+ **/
+static int user_minor;
+static int
+evms_ioctl_cmd_get_minor(void *arg)
+{
+	struct evms_user_minor_pkt tmp, *user_parms;
+	struct evms_logical_volume *lv;
+
+	user_parms = (struct evms_user_minor_pkt *) arg;
+	/* copy user's parameters to kernel space */
+	if (copy_from_user
+	    (&tmp.command, &user_parms->command, sizeof (tmp.command))) {
+		return -EFAULT;
+	}
+	if (tmp.command == EVMS_FIRST_VOLUME) {
+		user_minor = 0;
+		lv = NULL;
+	} else {	/* tmp.command == EVMS_NEXT_VOLUME */
+		lv = lookup_volume(user_minor);
+	}
+
+	tmp.status = EVMS_VOLUME_INVALID;
+	while ((lv = find_next_volume(lv))) {
+		user_minor = lv->minor;
+		/* see if any corrupt volumes have been
+		 * unmounted. If so, clean up the entry
+		 * in evms_logical_volumes, and
+		 * don't report the volume to the user.
+		 */
+		if (lv->flags & EVMS_VOLUME_CORRUPT) {
+			if (!is_busy(mk_kdev(EVMS_MAJOR, user_minor))) {
+				/* clear logical volume structure
+				   * for this volume so it may be
+				   * reused.
+				 */
+				LOG_WARNING("ioctl_get_minor: found unmounted "
+					    "%s volume(%u,%u,%s).\n",
+					    ((lv->flags & EVMS_VOLUME_SOFT_DELETED) ?
+					     "'soft deleted'" : ""),
+					    EVMS_MAJOR, user_minor, lv->name);
+				LOG_WARNING("     releasing minor(%d) used by "
+					    "volume(%s)!\n",
+					    user_minor, lv->name);
+				blk_cleanup_queue(&lv->request_queue);
+				kfree(lv->name);
+				list_del_init(&lv->volumes);
+				kfree(lv);
+			}
+		}
+		if (lv->node || (lv->flags & EVMS_VOLUME_CORRUPT)) {
+			tmp.status = EVMS_VOLUME_VALID;
+			tmp.minor = user_minor;
+			break;
+		}
+	}
+	/* copy info to userspace */
+	if (copy_to_user(user_parms, &tmp, sizeof (tmp)))
+		return -EFAULT;
+
+	return 0;
+}
+
+/**
+ * evms_ioctl_cmd_get_volume_data - retrieves info about a volume
+ * @arg:	vfs ioctl parameter, the get volume data ioctl packet
+ *
+ * retrieves the info about a specified volume
+ *
+ * returns: 0 on success
+ * 	    otherwise error code
+ **/
+static int
+evms_ioctl_cmd_get_volume_data(void *arg)
+{
+	int rc = 0;
+	struct evms_volume_data_pkt tmp, *user_parms;
+	struct evms_logical_volume *lv = NULL;
+
+	user_parms = (struct evms_volume_data_pkt *) arg;
+	/* copy user's parameters to kernel space */
+	if (copy_from_user(&tmp, user_parms, sizeof (tmp))) {
+		return -EFAULT;
+	}
+	lv = lookup_volume(tmp.minor);
+	if (lv == NULL || lv->node == NULL) {
+		rc = -ENODEV;
+		goto exit;
+	}
+	tmp.flags = lv->flags;
+	strcpy(tmp.volume_name, EVMS_DIR_NAME "/");
+	strcat(tmp.volume_name, lv->name);
+      exit:
+	/* copy return code and info to userspace */
+	tmp.status = rc;
+	if (copy_to_user(user_parms, &tmp, sizeof (tmp))) {
+		rc = -EFAULT;
+	}
+	return rc;
+}
+
+/**
+ * evms_ioctl_cmd_get_plugin - retrieves info about the 1st or next loaded kernel plugin
+ * @arg:	vfs ioctl parameter, the registered plugin ioctl packet
+ *
+ * retrieves the info about the 1st or next loaded kernel plugin
+ *
+ * returns: 0 on success
+ * 	    otherwise error code
+ **/
+static int plugin_id;
+static int
+evms_ioctl_cmd_get_plugin(void *arg)
+{
+	struct evms_kernel_plugin_pkt tmp, *user_parms;
+	struct evms_plugin_header *plugin;
+
+	user_parms = (struct evms_kernel_plugin_pkt *) arg;
+	/* copy user's parameters to kernel space */
+	if (copy_from_user
+	    (&tmp.command, &user_parms->command, sizeof (tmp.command))) {
+		return -EFAULT;
+	}
+	spin_lock(&plugin_lock);
+	plugin = list_entry(&plugin_head, typeof(*plugin), headers);
+	/* if the command is not 0, then verify
+	 * that ioctl_reg_record is pointing to
+	 * current and valid plugin header.
+	 */
+	if (tmp.command) {	/* tmp.command == EVMS_NEXT_PLUGIN */
+		list_for_each_entry(plugin, &plugin_head, headers) {
+			if (plugin_id == plugin->id) {
+				break;
+			}
+		}
+		if (&plugin->headers == &plugin_head) {
+			tmp.command = EVMS_FIRST_PLUGIN;
+		}
+	}
+	if (tmp.command == EVMS_FIRST_PLUGIN) {
+		plugin_id = 0;
+	}
+	if (!list_empty(&plugin_head)) {
+		plugin = list_entry(plugin->headers.next, typeof(*plugin), headers);
+		if (&plugin->headers != &plugin_head) {
+			plugin_id = plugin->id;
+		}
+	}
+	/* populate the user's buffer */
+	tmp.status = EVMS_PLUGIN_INVALID;
+	tmp.id = 0;
+	if (plugin_id) {
+		tmp.id = plugin->id;
+		tmp.version = plugin->version;
+		tmp.status = EVMS_PLUGIN_VALID;
+	}
+	spin_unlock(&plugin_lock);
+	/* copy info to userspace */
+	if (copy_to_user(user_parms, &tmp, sizeof (tmp))) {
+		return -EFAULT;
+	}
+	return 0;
+}
+
+/**
+ * evms_ioctl_cmd_plugin_ioctl - sends a plugin specific ioctl to a specified plugin
+ * @inode:	vfs ioctl parameter
+ * @file:	vfs ioctl parameter
+ * @cmd:	vfs ioctl parameter
+ * @arg:	vfs ioctl parameter, the plugin ioctl packet
+ *
+ * routes a plugin specific ioctl to the specified plugin
+ *
+ * returns: 0 on success
+ * 	    otherwise error code
+ **/
+static int
+evms_ioctl_cmd_plugin_ioctl(struct inode *inode,
+			    struct file *file, unsigned int cmd, ulong arg)
+{
+	int rc;
+	struct evms_plugin_ioctl_pkt tmp, *user_parms;
+	struct evms_plugin_header *plugin;
+
+	user_parms = (struct evms_plugin_ioctl_pkt *) arg;
+	/* copy user's parameters to kernel space */
+	if (copy_from_user(&tmp, user_parms, sizeof (tmp))) {
+		return -EFAULT;
+	}
+	/* search for the specified plugin */
+	rc = -ENOPKG;
+	spin_lock(&plugin_lock);
+	list_for_each_entry(plugin, &plugin_head, headers) {
+		/* check for the specified feature id */
+		if (plugin->id == tmp.feature_id) {
+			/* check that entry point is used */
+			rc = -ENOSYS;
+			if (plugin->fops->direct_ioctl) {
+				spin_unlock(&plugin_lock);
+				rc = DIRECT_IOCTL(plugin, inode, file, cmd, arg);
+				spin_lock(&plugin_lock);
+			}
+			break;
+		}
+	}
+	spin_unlock(&plugin_lock);
+	/* copy the status value back to the user */
+	tmp.status = rc;
+	if (copy_to_user(user_parms, &tmp, sizeof (tmp))) {
+		rc = -EFAULT;
+	}
+	return rc;
+}
+
+/**
+ * evms_ioctl_cmd_kernel_partial_csum - invokes the kernel's csum_partial routine 
+ * @arg:	vfs ioctl parameter, the compute csum ioctl packet
+ *
+ * provides userspace access to the kernel's csum_partial routine
+ *
+ * returns: 0 on success
+ * 	    otherwise error code
+ **/
+#define MAX_BUFFER_SIZE 65536
+static int
+evms_ioctl_cmd_kernel_partial_csum(void *arg)
+{
+	int rc = 0;
+	struct evms_compute_csum_pkt tmp, *user_parms;
+	u8 *user_buffer_ptr, *buffer = NULL;
+	u64 remaining_bytes, compute_size = MAX_BUFFER_SIZE;
+	unsigned int insum;
+
+	user_parms = (struct evms_compute_csum_pkt *) arg;
+	/* copy user's parameters to kernel space */
+	if (copy_from_user(&tmp, user_parms, sizeof (tmp))) {
+		return -EFAULT;
+	}
+	/* allocate a io buffer upto 64Kbytes in size */
+	if (tmp.buffer_size < MAX_BUFFER_SIZE) {
+		compute_size = tmp.buffer_size;
+	}
+	/* allocate buffer large enough to hold a single sector */
+	buffer = kmalloc(compute_size, GFP_KERNEL);
+	if (!buffer) {
+		rc = -ENOMEM;
+		goto exit;
+	}
+	/* perform io with specified disk */
+	insum = tmp.insum;
+	remaining_bytes = tmp.buffer_size;
+	user_buffer_ptr = tmp.buffer_address;
+	while (remaining_bytes) {
+		/* compute the compute_size for this pass */
+		compute_size = (remaining_bytes >= MAX_BUFFER_SIZE) ?
+		    MAX_BUFFER_SIZE : remaining_bytes;
+		/* copy into kernel from user data buffer */
+		if (copy_from_user(buffer, user_buffer_ptr, compute_size)) {
+			rc = -EFAULT;
+			goto exit;
+		}
+		/* compute the checksum for this pass */
+		tmp.outsum = csum_partial(buffer, tmp.buffer_size, insum);
+		/* set up for another possible pass */
+		insum = tmp.outsum;
+		/* update loop progress variables */
+		user_buffer_ptr += compute_size;
+		tmp.buffer_address += compute_size;
+		remaining_bytes -= compute_size;
+	}
+      exit:
+	/* if the sector_buffer was allocated, free it */
+	if (buffer)
+		kfree(buffer);
+
+	/* copy the status value back to the user */
+	tmp.status = rc;
+	if (copy_to_user(user_parms, &tmp, sizeof (tmp))) {
+		rc = -EFAULT;
+	}
+	return rc;
+}
+
+#undef MAX_BUFFER_SIZE
+
+/**
+ * evms_ioctl_cmd_get_bmap - computes the physical dev/sector pair from a logical dev/sector 
+ * @inode:	vfs ioctl parameter
+ * @file:	vfs ioctl parameter
+ * @cmd:	vfs ioctl parameter
+ * @arg:	vfs ioctl parameter, the get bmap ioctl packet
+ *
+ * takes a logical volume's device/sector pair and returns the physical device/sector. this
+ * is typically used by loaders such as lilo, to read system files using BIOS during boot.
+ *
+ * returns: 0 on success
+ * 	    otherwise error code
+ **/
+static int
+evms_ioctl_cmd_get_bmap(struct inode *inode,
+			struct file *file, unsigned int cmd, ulong arg)
+{
+	int rc;
+	struct evms_get_bmap_pkt tmp, *user_parms;
+	struct evms_logical_volume *lv;
+
+	user_parms = (struct evms_get_bmap_pkt *) arg;
+	/* copy user's parameters to kernel space */
+	if (copy_from_user(&tmp, user_parms, sizeof (tmp))) {
+		return -EFAULT;
+	}
+	/* pass the ioctl down the volume stack */
+	lv = lookup_volume(minor(inode->i_rdev));
+	rc = IOCTL(lv->node, inode, file, cmd, (ulong) & tmp);
+	/* copy the status value back to the user */
+	tmp.status = rc;
+	if (copy_to_user(user_parms, &tmp, sizeof (tmp))) {
+		rc = -EFAULT;
+	}
+	return rc;
+}
+
+/**
+ * evms_ioctl_cmd_process_notify_event - allows a process to register for a signal on an event
+ * @arg:	vfs ioctl parameter, the notify ioctl packet
+ *
+ * lets a process register for a signal notification when a specified kernel event occurs.
+ *
+ * returns: 0 on success
+ * 	    otherwise error code
+ **/
+static int
+evms_ioctl_cmd_process_notify_event(ulong arg)
+{
+	int rc = 0, found = 0;
+	struct evms_notify_pkt tmp, *user_parms;
+	struct evms_event *event = NULL;
+	struct evms_kevent *kevent = NULL;
+
+	user_parms = (struct evms_notify_pkt *) arg;
+	/* copy user's parameters to kernel space */
+	if (copy_from_user(&tmp, user_parms, sizeof (tmp))) {
+		return -EFAULT;
+	}
+	/* check to see if PID has already been registered
+	 * for this event.
+	 */
+	list_for_each_entry(kevent, &evms_notify_list, list) {
+		event = &kevent->uevent;
+		if ((event->pid == tmp.eventry.pid) &&
+		    (event->eventid == tmp.eventry.eventid)) {
+			found = 1;
+			break;
+		}
+	}
+	if (tmp.command) {	/* tmp.command == EVMS_REGISTER_EVENT */
+		/* registration code */
+		if (found) {
+			rc = -EBUSY;
+			LOG_ERROR("error(%d): pid(%d) already registered to "
+				  "receive signal(%d) on event(%d).\n", rc,
+				  tmp.eventry.pid, tmp.eventry.signo,
+				  tmp.eventry.eventid);
+		} else {
+			/* register this pid/event type */
+			kevent =
+			    kmalloc(sizeof (struct evms_kevent), GFP_KERNEL);
+			if (!kevent) {
+				rc = -ENOMEM;
+				LOG_ERROR("error(%d) allocating event "
+					  "structure.\n", rc);
+			} else {
+				kevent->uevent.pid = tmp.eventry.pid;
+				kevent->uevent.eventid = tmp.eventry.eventid;
+				kevent->uevent.signo = tmp.eventry.signo;
+				list_add(&kevent->list, &evms_notify_list);
+			}
+		}
+	} else {		/* tmp.command == EVMS_UNREGISTER_EVENT */
+		/* unregistration code */
+		if (!found) {
+			rc = -ENODATA;
+			LOG_ERROR("error(%d) attempting to unregister a "
+				  "non-registered pid(%d) on event(%d).\n",
+				  rc, tmp.eventry.pid, tmp.eventry.eventid);
+		} else {
+			list_del_init(&kevent->list);
+			kfree(event);
+		}
+	}
+	/* copy the status value back to the user */
+	tmp.status = rc;
+	if (copy_to_user(user_parms, &tmp, sizeof (tmp))) {
+		rc = -EFAULT;
+	}
+	return rc;
+}
+
+/**
+ * evms_ioctl_cmd_check_mount_status - determines if the specified volume is current mounted
+ * @inode:	vfs ioctl parameter
+ * @file:	vfs ioctl parameter
+ * @arg:	vfs ioctl parameter, the mount status ioctl packet
+ *
+ * allows userspace to query the kernel to know if the specified volume is mounted (in use)
+ *
+ * returns: 0 on success
+ * 	    otherwise error code
+ **/
+static int
+evms_ioctl_cmd_check_mount_status(struct inode *inode, struct file *file,
+				  ulong arg)
+{
+	struct evms_mount_status_pkt tmp, *user_parms;
+
+	user_parms = (struct evms_mount_status_pkt *) arg;
+	/* copy user's parameters to kernel space */
+	if (copy_from_user(&tmp, user_parms, sizeof (tmp))) {
+		return -EFAULT;
+	}
+	tmp.mounted = (is_busy(mk_kdev(EVMS_MAJOR, tmp.minor))) ? 1 : 0;
+	/* copy the status value back to the user */
+	tmp.status = 0;
+	if (copy_to_user(user_parms, &tmp, sizeof (tmp))) {
+		return -EFAULT;
+	}
+	return 0;
+}
+
+/************************************************/
+/* END -- IOCTL commands -- EVMS specific       */
+/************************************************/
+
+/************************************************/
+/* START -- IOCTL commands -- Volume specific   */
+/************************************************/
+
+/************************************************/
+/* END -- IOCTL commands -- Volume specific     */
+/************************************************/
+
+/************************************************/
+/* START -- IOCTL main                          */
+/************************************************/
+
+/**
+ * evms_ioctl - the main ioctl routing function
+ * @inode:	vfs ioctl parameter, the inode
+ * @file:	vfs ioctl parameter, the file
+ * @cmd:	vfs ioctl parameter, the command
+ * @arg:	vfs ioctl parameter, the argument
+ *
+ * EVMS' main ioctl entry point and routing function
+ *
+ * returns: 0 on success
+ * 	    otherwise error code
+ **/
+static int
+evms_ioctl(struct inode *inode, struct file *file, unsigned int cmd, ulong arg)
+{
+	ulong minor = 0;
+	int rc = 0;
+
+	/* check user access */
+	if (!capable(CAP_SYS_ADMIN))
+		return -EACCES;
+
+	if (!inode)
+		return -EINVAL;
+
+	/* get the minor */
+	minor = minor(inode->i_rdev);
+	LOG_EXTRA("ioctl: minor(%lu), dir(%d), size(%d), type(%d), nr(%d)\n",
+		  minor, (cmd >> _IOC_DIRSHIFT) & _IOC_DIRMASK,
+		  (cmd >> _IOC_SIZESHIFT) & _IOC_SIZEMASK,
+		  (cmd >> _IOC_TYPESHIFT) & _IOC_TYPEMASK,
+		  (cmd >> _IOC_NRSHIFT) & _IOC_NRMASK);
+
+	/* process the IOCTL commands */
+	if (!minor) {
+		/* process all EVMS specific commands */
+		switch (cmd) {
+		case EVMS_GET_IOCTL_VERSION:
+			rc = evms_ioctl_cmd_get_ioctl_version((void *)
+							      arg);
+			break;
+		case EVMS_GET_VERSION:
+			rc = evms_ioctl_cmd_get_version((void *) arg);
+			break;
+		case EVMS_GET_INFO_LEVEL:
+			rc = evms_ioctl_cmd_get_info_level((void *)
+							   arg);
+			break;
+		case EVMS_SET_INFO_LEVEL:
+			rc = evms_ioctl_cmd_set_info_level((void *)
+							   arg);
+			break;
+		case EVMS_REDISCOVER_VOLUMES:
+			rc = evms_ioctl_cmd_rediscover_volumes(inode,
+							       file, cmd, arg);
+			break;
+		case EVMS_GET_LOGICAL_DISK:
+			rc = evms_ioctl_cmd_get_logical_disk((void *)
+							     arg);
+			break;
+		case EVMS_GET_LOGICAL_DISK_INFO:
+			rc = evms_ioctl_cmd_get_logical_disk_info((void *)
+								  arg);
+			break;
+		case EVMS_SECTOR_IO:
+			rc = evms_ioctl_cmd_sector_io((void *) arg);
+			break;
+		case EVMS_GET_MINOR:
+			rc = evms_ioctl_cmd_get_minor((void *) arg);
+			break;
+		case EVMS_GET_VOLUME_DATA:
+			rc = evms_ioctl_cmd_get_volume_data((void *)
+							    arg);
+			break;
+		case EVMS_DELETE_VOLUME:
+			rc = evms_ioctl_cmd_delete_volume(inode, file, arg);
+			break;
+		case EVMS_GET_PLUGIN:
+			rc = evms_ioctl_cmd_get_plugin((void *) arg);
+			break;
+		case EVMS_PLUGIN_IOCTL:
+			rc = evms_ioctl_cmd_plugin_ioctl(inode, file, cmd, arg);
+			break;
+		case EVMS_COMPUTE_CSUM:
+			rc = evms_ioctl_cmd_kernel_partial_csum((void *)
+								arg);
+			break;
+		case EVMS_PROCESS_NOTIFY_EVENT:
+			rc = evms_ioctl_cmd_process_notify_event(arg);
+			break;
+		case EVMS_CHECK_MOUNT_STATUS:
+			rc = evms_ioctl_cmd_check_mount_status(inode, file,
+							       arg);
+			break;
+		default:
+			rc = -ENOTTY;
+			break;
+		}
+	} else {
+		struct evms_logical_volume *lv;
+		struct evms_logical_node *node = NULL;
+
+		/* insure this minor points to a valid volume */
+		lv = lookup_volume(minor);
+		if (lv == NULL || lv->node == NULL) {
+			return -ENXIO;
+		}
+		node = lv->node;
+		
+		/* process Volume specific commands */
+		switch (cmd) {
+		case EVMS_GET_IOCTL_VERSION:
+			rc = evms_ioctl_cmd_get_ioctl_version((void *)
+							      arg);
+			break;
+		case EVMS_GET_BMAP:
+			rc = evms_ioctl_cmd_get_bmap(inode, file, cmd, arg);
+			break;
+		default:
+			rc = IOCTL(node, inode, file, cmd, arg);
+			break;
+		}
+	}
+	return rc;
+}
+
+/************************************************/
+/* END -- IOCTL main                            */
+/************************************************/
+
+/************************************************/
+/* START -- CHECK MEDIA CHANGE		        */
+/************************************************/
+
+/**
+ * evms_check_media_change
+ * @dev:	the device to check
+ *
+ * checks to see if the media change flag is set for this device
+ *
+ * returns: 1 = media change detected
+ *	    0 = no media change detected
+ * 	    otherwise error code
+ **/
+static int
+evms_check_media_change(kdev_t dev)
+{
+	int rc = 0;
+	struct evms_logical_volume *lv;
+
+	lv = lookup_volume(minor(dev));
+	if (lv == NULL || lv->node == NULL) {
+		return -ENXIO;
+	}
+	/* Wait here if the volume is quiesced. */
+	atomic_inc(&lv->requests_in_progress);
+	do {
+		if (!lv->quiesced)
+			break;
+		if (atomic_dec_and_test(&lv->requests_in_progress))
+			wake_up(&lv->quiesce_wait_queue);
+		wait_event(lv->request_wait_queue, (!lv->quiesced));
+		atomic_inc(&lv->requests_in_progress);
+	} while (0);
+	/* Volume node may have gone away while we were waiting. */
+	if (!lv->node) {
+		return -ENXIO;
+	}
+	if (lv->flags & EVMS_DEVICE_REMOVABLE) {
+		rc = CHECK_MEDIA_CHANGE(lv->node, dev);
+	}
+	if (atomic_dec_and_test(&lv->requests_in_progress) &&
+	    lv->quiesced) {
+		wake_up(&lv->quiesce_wait_queue);
+	}
+	if (rc < 0) {
+		LOG_ERROR("error(%d) checking media change on '%s'.\n", 
+			  rc, lv->name);
+	}
+	return rc;
+}
+
+/************************************************/
+/* END -- CHECK MEDIA CHANGE		        */
+/************************************************/
+
+/**
+ * evms_check_for_device_changes - looks for any new/removed devices/media
+ * @inode:	vfs ioctl parameter, the inode
+ * @file:	vfs ioctl parameter, the file
+ *
+ * checks for new or removed devices as well as changed removable media. to accomplish
+ * this, this routine will completely purge from kernel memory entire volumes that may
+ * only partially reside on the change devices. once purged, all new devices and devices
+ * that purged volumes resided on, are re-probed and the resulting volumes are made
+ * available.
+ *
+ * NOTE: to maximize the effectiveness of this function, it is called on every OPEN
+ * of the EVMS block_device. Everytime the user tools are invoked, or a volume is
+ * mounted, this check will happen and the user will the see the updated results 
+ * without any manual steps.
+ *
+ * returns: 0 on success
+ * 	    otherwise error code
+ **/
+static int
+evms_check_for_device_changes(struct inode *inode, struct file *file)
+{
+	int rc = 0, something_changed = 0, i;
+	struct evms_rediscover_pkt kernel_rd_pckt = { 0, 0, NULL };
+	struct list_head changed_list, new_device_list;
+	struct evms_logical_node *disk, *next;
+	struct evms_logical_volume *lv = NULL, *next_lv;
+
+	/* grab the rediscover semaphore */
+	down(&red_sem);
+
+	INIT_LIST_HEAD(&changed_list);
+	INIT_LIST_HEAD(&new_device_list);
+
+	/* check for new devices
+	 *
+	 * put all new devices on the disk list so they
+	 * will be included in the rediscovery process.
+	 */
+	evms_discover_logical_disks(&new_device_list);
+	if (!list_empty(&new_device_list)) {
+		LOG_DETAILS("%s: new devices detected.\n", __FUNCTION__);
+		something_changed++;
+		/* put these new nodes on the disk list */
+		list_for_each_entry_safe(disk, next, &new_device_list, discover) {
+			list_del_init(&disk->discover);
+			list_add(&disk->removable, &changed_list);
+		}
+	}
+
+	/* check all devices for changed removable media
+	 *
+	 * scan the evms device list and issue check
+	 * media change on each removable media device.
+	 * put all removable devices that indicate a
+	 * media change on the changed list.
+	 *
+	 * also scan for devices that have been unplugged
+	 * or contain corrupt volumes.
+	 */
+	list_for_each_entry_safe(disk, next, &evms_device_list, device) {
+		int add_to_list = 0;
+		/* only really check removable media devices */
+		if (disk->flags & EVMS_DEVICE_REMOVABLE) {
+			/* check for media change */
+			rc = CHECK_MEDIA_CHANGE(disk, mk_kdev(0,0));
+			if (rc < 0) {
+				LOG_ERROR("error(%d) doing CHECK_MEDIA_CHANGE "
+					  "on '%s'.\n", rc, disk->name);
+			} else if (rc == 1) {
+				add_to_list = 1;
+			}
+		}
+		/* check for devices that were present
+		 * earlier, but are gone now (from being 
+		 * unplugged or unloaded driver).
+		 */
+		if (disk->flags & EVMS_DEVICE_UNAVAILABLE) {
+			add_to_list = 1;
+		}
+		if (add_to_list) {
+			something_changed++;
+			list_add(&disk->removable, &changed_list);
+		}
+	}
+	/* log a statement that we detected changed media.
+	 */
+	if (!list_empty(&changed_list)) {
+		LOG_DETAILS("%s: media change detected.\n", __FUNCTION__);
+	}
+
+	/* check for volumes with removed removable media.
+	 * mark the volumes that reside on changed media.
+	 */
+	lv = NULL;
+	while ((lv = find_next_volume(lv))) {
+		if (!lv->node)
+			continue;
+		if (!(lv->flags & EVMS_DEVICE_REMOVABLE))
+			continue;
+		if (evms_check_media_change(mk_kdev(EVMS_MAJOR, lv->minor)) <= 0)
+			continue;
+		/* remember which volumes have changed media */
+		lv->flags |= EVMS_MEDIA_CHANGED;
+		something_changed++;
+	}
+
+	/* check for removed devices */
+	lv = NULL;
+	while ((lv = find_next_volume(lv))) {
+		int status;
+		if (!lv->node)
+			continue;
+		/* check for device status */
+		status = 0;
+		rc = DEVICE_STATUS(lv->node, &status);
+		if (rc) {
+			LOG_ERROR("error(%d) doing DEVICE_STATUS "
+				  "on '%s'.\n", rc, lv->name);
+			continue;
+		}
+		if (!(status & EVMS_DEVICE_UNAVAILABLE)) {
+			continue;
+		}
+		/* remember which volumes have changed media */
+		lv->flags |= EVMS_DEVICE_UNPLUGGED;
+		something_changed++;
+	}
+
+	/* do we have some work to do? */
+	if (something_changed) {
+		/* check for volumes to be deleted */
+		lv = NULL;
+		while ((lv = find_next_volume(lv))) {
+			if (!lv->node)
+				continue;
+			/* only proceed on volumes with:
+			 *  changed media,
+			 *  hot-unplugged devices,
+			 *  & partial volumes
+			 */
+			if (!(lv->flags &
+			      (EVMS_MEDIA_CHANGED |
+			       EVMS_VOLUME_PARTIAL | EVMS_DEVICE_UNPLUGGED)))
+				continue;
+			/* gather the disk's needing to be
+			 * rediscovered to rebuild this
+			 * volume.
+			 *
+			 * this will locate other disks that
+			 * the volume resides on that don't
+			 * indicate media change.
+			 */
+			rc = DEVICE_LIST(lv->node, &changed_list);
+			if (rc) {
+				LOG_ERROR("%s: error(%d) retrieving underlying "
+					  "disk list for '%s', skipping ...\n",
+					  __FUNCTION__, rc, lv->name);
+				continue;
+			}
+			/* quiesce all the changed volumes
+			 * prior to being deleted.
+			 */
+			rc = evms_quiesce_volume(lv, 1, lv->minor, 0);
+			if (rc) {
+				LOG_ERROR("%s: error(%d) attempting to quiesce "
+					  "'%s%s'.\n", __FUNCTION__, rc,
+					  EVMS_DIR_NAME "/", lv->name);
+			}
+		}
+
+		/* we need to revalidate all the changed
+		 * media. this is accomplished by issuing
+		 * the revalidate disk ioctl to each device
+		 * with changed media. the device manager
+		 * remembers which devices indicated
+		 * media changed (set by check media
+		 * changed ioctl issued earlier), and will
+		 * only issue the revalidate disk ioctl to
+		 * those disks one time.
+		 *
+		 * NOTE:
+		 * this needs to be done BEFORE deleting
+		 * the volumes because deleting the
+		 * last segment on disk will cause the
+		 * associated disk node to freed, and we
+		 * will not be able to issue the
+		 * revalidate disk ioctl after that.
+		 */
+		list_for_each_entry(disk, &changed_list, removable) {
+			/* only really do removable media devices */
+			if (disk->flags & EVMS_MEDIA_CHANGED) {
+				/* go revalidate the change media */
+				rc = REVALIDATE(disk, mk_kdev(0,0));
+				if (rc) {
+					LOG_ERROR("%s: error(%d) attempting to "
+						  "revalidate '%s%s'.\n",
+						  __FUNCTION__, rc,
+						  EVMS_DIR_NAME "/", lv->name);
+				}
+			}
+		}
+
+		/* delete all the affected volumes */
+		next_lv = NULL;
+		while ((lv = find_next_volume_safe(&next_lv))) {
+			if (!lv->node)
+				continue;
+			/* only proceed on volumes with:
+			 *  changed media,
+			 *  hot-unplugged devices,
+			 *  & partial volumes
+			 */
+			if (!(lv->flags &
+			      (EVMS_MEDIA_CHANGED |
+			       EVMS_VOLUME_PARTIAL | EVMS_DEVICE_UNPLUGGED)))
+				continue;
+			/* only delete quiesced volumes */
+			if (!lv->quiesced)
+				continue;
+			/* delete the volume from memory.
+			 * do a 'soft' delete if volume
+			 * is mounted, and 'hard' delete
+			 * if it is not.
+			 *
+			 * NOTE: the delete operation will
+			 * clear the bits in the flags field.
+			 */
+			rc = evms_delete_volume(lv, 2, lv->minor, 0);
+		}
+
+		/* at this point all devices indicating
+		 * media change that had volumes on them
+		 * should be gone. however, we could still
+		 * have devices indicating media change
+		 * that had no volumes on them in the disk
+		 * list. we need to delete these devices
+		 * from kernel memory and the global device
+		 * list.
+		 */
+		list_for_each_entry_safe(disk, next, &evms_device_list, device) {
+			if (disk->flags & EVMS_MEDIA_CHANGED) {
+				DELETE(disk);
+			}
+		}
+
+		/* all the devices that indicated media
+		 * change should be gone, both from kernel
+		 * memory and evms device list. we now
+		 * need to remove any references to these
+		 * devices from the disk list.
+		 *
+		 * when removable media is installed, it
+		 * will get detected in the device manager's
+		 * rediscovery as a new device and added to
+		 * the discover list.
+		 */
+		list_for_each_entry_safe(disk, next, &changed_list, removable) {
+			int still_in_changed_list;
+			struct evms_logical_node *edl_node;
+
+			still_in_changed_list = 0;
+			list_for_each_entry(edl_node, &evms_device_list, device) {
+				if (edl_node == disk) {
+					still_in_changed_list = 1;
+					break;
+				}
+			}
+			if (still_in_changed_list == 0) {
+				list_del_init(&disk->removable);
+			}
+		}
+
+		/* build the in-kernel rediscover packet */
+
+		/* allocate the space for the drive_array in
+		 * the struct evms_rediscover packet. to do this
+		 * we need to count the number of disk nodes,
+		 * then allocate the necessary space.
+		 */
+		/* count the disk nodes */
+		list_for_each_entry(disk, &changed_list, removable) {
+			kernel_rd_pckt.drive_count++;
+		}
+		/* allocate the space */
+		if (kernel_rd_pckt.drive_count) {
+			kernel_rd_pckt.drive_array
+			    = kmalloc(kernel_rd_pckt.drive_count *
+				      sizeof (u64), GFP_KERNEL);
+			if (!kernel_rd_pckt.drive_array) {
+				rc = -ENOMEM;
+				LOG_ERROR("%s: error(%d) allocating rediscover "
+					  "drive array.\n", __FUNCTION__, rc);
+				return rc;
+			}
+		}
+		/* populate the drive array
+		 *
+		 * this also frees the changed_list which is useful
+		 * if we had an error allocating the drive array.
+		 */
+		i = 0;
+		list_for_each_entry_safe(disk, next, &changed_list, removable) {
+			/* remove this disk from the disk list */
+			list_del_init(&disk->removable);
+			/* add this disk to rediscover packet
+			 */
+			kernel_rd_pckt.drive_array[i++] =
+			    NODE_TO_DEV_HANDLE(disk);
+		}
+		/* perform the rediscovery operation */
+		rc = evms_discover_volumes(&kernel_rd_pckt);
+		if (kernel_rd_pckt.drive_count) {
+			kfree(kernel_rd_pckt.drive_array);
+		}
+		LOG_DETAILS("%s: rediscover completed.\n", __FUNCTION__);
+	}
+
+	/* release the rediscover semaphore */
+	up(&red_sem);
+
+	return rc;
+}
+
+/************************************************/
+/* START -- REVALIDATE DISK		        */
+/************************************************/
+
+/**
+ * evms_revalidate_disk - routes revalidate ioctls to all underlying volume devices
+ * @dev:	the volume whose underlying devices will be revalidated
+ *
+ * ripples the revalidate request down the EVMS volume feature stack and routes to
+ * all underlying dasd devices. This is typically done after issuing a change
+ * media change which reports a media change, so the kernel can update its disk
+ * info for the new media.
+ *
+ * returns: 0 on success
+ * 	    otherwise error code
+ **/
+static int
+evms_revalidate_disk(kdev_t dev)
+{
+	int rc;
+	struct evms_logical_volume *lv;
+
+	lv = lookup_volume(minor(dev));
+	if (lv == NULL || lv->node == NULL) {
+		return -ENXIO;
+	}
+	/* Wait here if the volume is quiesced. */
+	atomic_inc(&lv->requests_in_progress);
+	do {
+		if (!lv->quiesced)
+			break;
+		if (atomic_dec_and_test(&lv->requests_in_progress))
+			wake_up(&lv->quiesce_wait_queue);
+		wait_event(lv->request_wait_queue, (!lv->quiesced));
+		atomic_inc(&lv->requests_in_progress);
+	} while (0);
+	/* Volume node may have gone away while we were waiting. */
+	if (!lv->node) {
+		return -ENXIO;
+	}
+	rc = REVALIDATE(lv->node, dev);
+	if (atomic_dec_and_test(&lv->requests_in_progress) &&
+	    lv->quiesced) {
+		wake_up(&lv->quiesce_wait_queue);
+	}
+	return rc;
+}
+
+/************************************************/
+/* END -- REVALIDATE DISK		        */
+/************************************************/
+
+/************************************************/
+/* START -- OPEN			        */
+/************************************************/
+
+/**
+ * evms_open - open a volume or EVMS block device
+ * @inode:	vfs ioctl parameter, the inode
+ * @file:	vfs ioctl parameter, the file
+ *
+ * opens a volume or the EVMS block device. this will also check for device changes
+ * prior to rippling the OPEN request down the volume feature stack to the underlying
+ * devices.
+ *
+ * returns: 0 on success
+ * 	    otherwise error code
+ **/
+static int
+evms_open(struct inode *inode, struct file *file)
+{
+	int minor, rc;
+	struct evms_logical_volume *lv;
+
+	if (!capable(CAP_SYS_ADMIN))
+		return -EACCES;
+	if (!inode)
+		return -EINVAL;
+	rc = evms_check_for_device_changes(inode, file);
+	if (rc) {
+		return rc;
+	}
+	minor = minor(inode->i_rdev);
+	if (minor) {
+		lv = lookup_volume(minor);
+		if (lv == NULL || lv->node == NULL) {
+			return -ENXIO;
+		}
+		/* Wait here if the volume is quiesced. */
+		atomic_inc(&lv->requests_in_progress);
+		do {
+			if (!lv->quiesced)
+				break;
+			if (atomic_dec_and_test(&lv->requests_in_progress))
+				wake_up(&lv->quiesce_wait_queue);
+			wait_event(lv->request_wait_queue, (!lv->quiesced));
+			atomic_inc(&lv->requests_in_progress);
+		} while (0);
+		/* Volume node may have gone away while we were waiting. */
+		if (!lv->node) {
+			return -ENXIO;
+		}
+		rc = OPEN(lv->node, inode, file);
+		if (atomic_dec_and_test(&lv->requests_in_progress) &&
+		    lv->quiesced) {
+			wake_up(&lv->quiesce_wait_queue);
+		}
+		if (rc) {
+			LOG_ERROR("error(%d) opening volume '%s'.\n", 
+				  rc, lv->name);
+			return rc;
+		}
+	}
+	return 0;
+}
+
+/************************************************/
+/* END -- OPEN				        */
+/************************************************/
+
+/************************************************/
+/* START -- RELEASE			        */
+/************************************************/
+
+/**
+ * evms_release - release or close a volume or EVMS block device
+ * @inode:	vfs ioctl parameter, the inode
+ * @file:	vfs ioctl parameter, the file
+ *
+ * releases a volume or the EVMS block device, rippling the RELEASE request
+ * down the volume feature stack to the underlying devices.
+ *
+ * returns: 0 on success
+ * 	    otherwise error code
+ **/
+static int
+evms_release(struct inode *inode, struct file *file)
+{
+	int minor;
+	struct evms_logical_volume *lv = NULL;
+
+	if (!inode)
+		return -EINVAL;
+	minor = minor(inode->i_rdev);
+	if (minor) {
+		int rc;
+		lv = lookup_volume(minor);
+		if (lv == NULL || lv->node == NULL) {
+			return -ENXIO;
+		}
+		/* Wait here if the volume is quiesced. */
+		atomic_inc(&lv->requests_in_progress);
+		do {
+			if (!lv->quiesced)
+				break;
+			if (atomic_dec_and_test(&lv->requests_in_progress))
+				wake_up(&lv->quiesce_wait_queue);
+			wait_event(lv->request_wait_queue, (!lv->quiesced));
+			atomic_inc(&lv->requests_in_progress);
+		} while (0);
+		/* Volume node may have gone away while we were waiting. */
+		if (!lv->node) {
+			return -ENXIO;
+		}
+		rc = CLOSE(lv->node, inode, file);
+		if (atomic_dec_and_test(&lv->requests_in_progress) &&
+		    lv->quiesced) {
+			wake_up(&lv->quiesce_wait_queue);
+		}
+		if (rc) {
+			LOG_ERROR("error(%d) releasing volume '%s'.\n", 
+				  rc, lv->name);
+			return rc;
+		}
+	}
+	return 0;
+}
+
+/************************************************/
+/* END -- RELEASE			        */
+/************************************************/
+
+/**
+ * evms_fops - EVMS block device operations table
+ **/
+struct block_device_operations evms_fops = {
+	owner:THIS_MODULE,
+	open:evms_open,
+	release:evms_release,
+	ioctl:evms_ioctl,
+	check_media_change:evms_check_media_change,
+	revalidate:evms_revalidate_disk
+};
+
+/**********************************************************/
+/* END -- FOPS functions definitions                      */
+/**********************************************************/
+
+/**********************************************************/
+/* START -- RUNTIME support functions                     */
+/**********************************************************/
+
+/**
+ * evms_find_queue - finds the appropriate per volume request queue
+ * @dev:	the volume whose queue to find
+ *
+ * locates the request queue for the specified volume
+ *
+ * returns: NULL if request queue not found
+ *          volume's request queue
+ **/
+request_queue_t *
+evms_find_queue(kdev_t dev)
+{
+	struct evms_logical_volume *lv = lookup_volume(minor(dev));
+	if (lv && lv->node)
+		return &lv->request_queue;
+	return NULL;
+}
+EXPORT_SYMBOL(evms_find_queue);
+
+/**
+ * evms_make_request_fn - IO request submission routine for EVMS
+ * @q:		request queue
+ * @bio:	submitted bio
+ *
+ * routes the bio to the appropriate volume's submit_io entry point
+ *
+ * returns: 0 always
+ **/
+int
+evms_make_request_fn(request_queue_t * q, struct bio *bio)
+{
+	struct evms_logical_volume *lv = 
+		lookup_volume(minor(to_kdev_t(bio->bi_bdev->bd_dev)));
+	if (!lv || !lv->node) {
+		goto error;
+	}
+
+	/* Wait here if the volume is quiesced. */
+	atomic_inc(&lv->requests_in_progress);
+	do {
+		if (!lv->quiesced)
+			break;
+		if (atomic_dec_and_test(&lv->requests_in_progress))
+			wake_up(&lv->quiesce_wait_queue);
+		wait_event(lv->request_wait_queue, (!lv->quiesced));
+		atomic_inc(&lv->requests_in_progress);
+	} while (0);
+
+	/* Volume node may have gone away while we were waiting. */
+	if (!lv->node) {
+		goto error;
+	}
+
+	SUBMIT_IO(lv->node, bio);
+
+	if (atomic_dec_and_test(&lv->requests_in_progress) &&
+	    lv->quiesced) {
+		wake_up(&lv->quiesce_wait_queue);
+	}
+	return 0;
+
+error:
+	LOG_ERROR("request for unknown logical volume [minor(%d)].\n",
+		  minor(to_kdev_t(bio->bi_bdev->bd_dev)));
+	bio_io_error(bio, bio->bi_size);
+	return 0;
+}
+
+/**********************************************************/
+/* END -- RUNTIME support functions                       */
+/**********************************************************/
+
+/**
+ * evms_notify_reboot - EVMS' reboot notification response function
+ * @this:	notifer_block
+ * @code:	reboot reason code
+ * @x:		unused here
+ *
+ * this function gets called at shutdown time and is used
+ * to remove any evms controlled volumes from memory, thus
+ * allowing any plugins needing to flush internal caches
+ * to do so.
+ *
+ * returns: NOTIFY_DONE
+ */
+int
+evms_notify_reboot(struct notifier_block *this, ulong code, void *x)
+{
+	struct evms_logical_volume *lv, *next_lv;
+
+	switch (code) {
+	case SYS_DOWN:
+	case SYS_HALT:
+	case SYS_POWER_OFF:
+		LOG_DEFAULT("stopping all evms controlled volumes.\n");
+
+		/* quiesce all volumes */
+		lv = NULL;
+		while ((lv = find_next_volume(lv))) {
+			if (!lv->node) {
+				continue;
+			}
+			evms_quiesce_volume(lv, 1, lv->minor, 0);
+		}
+		/* delete all volumes
+		 *
+		 * to ensure this work under the
+		 * most circumstances, a "soft"
+		 * delete will be done. this will
+		 * handle the strange case of a
+		 * volume still being mounted.
+		 */
+		next_lv = NULL;
+		while ((lv = find_next_volume_safe(&next_lv))) {
+			if (!lv->node)
+				continue;
+			/* only delete quiesced volumes */
+			if (!lv->quiesced)
+				continue;
+			/* delete the volume from memory.
+			 * do a 'soft' delete if volume
+			 * is mounted, and 'hard' delete
+			 * if it is not.
+			 */
+			evms_delete_volume(lv, 2, lv->minor, 0);
+		}
+	}
+	return NOTIFY_DONE;
+}
+
+/**
+ * evms_notifier - reboot notification registration record
+ **/
+static struct notifier_block evms_notifier = {
+	.notifier_call = evms_notify_reboot,
+	.next = NULL,
+	.priority = INT_MAX,	/* before any real devices */
+};
+
+/* Hooks to be able to force-load the Passthru plugin. */
+extern int evms_passthru_init(void);
+extern void evms_passthru_exit(void);
+
+void evms_load_passthru(void)
+{
+	evms_passthru_init();
+}
+
+void evms_unload_passthru(void)
+{
+	evms_passthru_exit();
+}
+
+/**
+ * slab_pool_alloc
+ * @gfp_mask:	GFP allocation flag
+ * @data:	mempool prototype required fields
+ *
+ * mempool allocate function
+ **/
+static void *
+slab_pool_alloc(int gfp_mask, void *data)
+{
+	return kmem_cache_alloc(data, gfp_mask);
+}
+
+/**
+ * slab_pool_free
+ * @ptr:	mempool prototype required fields
+ * @data:	mempool prototype required fields
+ *
+ * mempool free function
+ **/
+static void
+slab_pool_free(void *ptr, void *data)
+{
+	kmem_cache_free(data, ptr);
+}
+
+int evms_can_unload(void)
+{
+	return (atomic_read(&red_sem.count) < 1) ? -EBUSY : 0;
+}
+
+/**
+ * evms_init_module
+ *
+ * This function runs once at module initialization and performs the one time
+ * EVMS setup tasks of allocating memory, initializing variables, and
+ * registering the block device.
+ **/
+static int __init
+evms_init_module(void)
+{
+	int rc;
+
+	LOG_DEFAULT("EVMS v%d.%d.%d initializing .... info level(%d).\n",
+		    EVMS_MAJOR_VERSION, EVMS_MINOR_VERSION,
+		    EVMS_PATCHLEVEL_VERSION, evms_info_level);
+
+	INIT_LIST_HEAD(&evms_device_list);
+	INIT_LIST_HEAD(&evms_fbottom_list);
+	INIT_LIST_HEAD(&evms_notify_list);
+	INIT_LIST_HEAD(&evms_logical_volumes);
+	init_MUTEX(&red_sem);
+
+	/* Initialize the evms_io_notify pool */
+	evms_io_notify_slab = kmem_cache_create("EVMS IO Notify",
+						sizeof (struct evms_io_notify),
+						0, SLAB_HWCACHE_ALIGN,
+						NULL, NULL);
+	if (!evms_io_notify_slab) {
+		LOG_ERROR("error(%d): unable to create EVMS IO Notify cache.",
+			  -ENOMEM);
+		return -ENOMEM;
+	}
+	evms_io_notify_pool = mempool_create(256, slab_pool_alloc,
+					     slab_pool_free,
+					     evms_io_notify_slab);
+	if (!evms_io_notify_pool) {
+		kmem_cache_destroy(evms_io_notify_slab);
+		LOG_ERROR("error(%d): unable to create EVMS IO Notify pool.",
+			  -ENOMEM);
+		return -ENOMEM;
+	}
+
+	/* Register the block device */
+	rc = register_blkdev(EVMS_MAJOR, EVMS_DIR_NAME, &evms_fops);
+	if (rc) {
+		LOG_CRITICAL("error(%d) calling register_blkdev()\n", rc);
+		return -EINVAL;
+	}
+
+	/* Register with devfs. A NULL return is not fatal,
+	 * since devfs just might not be running.
+	 */
+        evms_dir_devfs_handle = devfs_mk_dir(NULL, EVMS_DIR_NAME, NULL);
+	if (evms_dir_devfs_handle) {
+		evms_blk_devfs_handle =
+		    devfs_register(evms_dir_devfs_handle, EVMS_DEV_NAME,
+				   DEVFS_FL_DEFAULT, EVMS_MAJOR, 0,
+				   S_IFBLK | S_IRUGO | S_IWUGO,
+				   &evms_fops, NULL);
+		if (!evms_blk_devfs_handle) {
+			LOG_DETAILS("NULL return from devfs_register() for \"%s\"\n",
+				    EVMS_DEV_NAME);
+		}
+	}
+
+	blk_dev[EVMS_MAJOR].queue = evms_find_queue;
+	blk_queue_make_request(BLK_DEFAULT_QUEUE(EVMS_MAJOR),
+			       evms_make_request_fn);
+
+	/* Create the proc and sysctl entries. */
+	evms_cs_get_evms_proc_dir();
+	if (evms_proc_dir) {
+		create_proc_read_entry("info", 0, evms_proc_dir,
+				       evms_info_read_proc, NULL);
+		create_proc_read_entry("plugins", 0, evms_proc_dir,
+				       evms_plugins_read_proc, NULL);
+		create_proc_read_entry("volumes", 0, evms_proc_dir,
+				       evms_volumes_read_proc, NULL);
+	}
+
+	evms_table_header = register_sysctl_table(dev_dir_table, 1);
+
+	register_reboot_notifier(&evms_notifier);
+
+	evms_ioctl32_register();
+
+	evms_load_passthru();
+
+	return 0;
+}
+
+/**
+ * evms_exit_module
+ *
+ * This function runs when the EVMS block device is being unloaded. it
+ * unloads the passthru plugin, unregisters the ioctl32 handlers and the
+ * reboot notifier. Then it cleans up the proc and sysctl entries and the
+ * I/O request queue. Finally it unregisters with devfs and the block
+ * layer and frees up all allocated memory.
+ **/
+static void __exit
+evms_exit_module(void)
+{
+	LOG_DEFAULT("EVMS v%d.%d.%d unloading ....\n",
+		    EVMS_MAJOR_VERSION,
+		    EVMS_MINOR_VERSION, EVMS_PATCHLEVEL_VERSION);
+
+	evms_unload_passthru();
+
+	evms_ioctl32_unregister();
+
+	unregister_reboot_notifier(&evms_notifier);
+
+	if (evms_proc_dir) {
+		remove_proc_entry("volumes", evms_proc_dir);
+		remove_proc_entry("plugins", evms_proc_dir);
+		remove_proc_entry("info", evms_proc_dir);
+		remove_proc_entry("evms", NULL);
+	}
+
+	unregister_sysctl_table(evms_table_header);
+
+	blk_cleanup_queue(BLK_DEFAULT_QUEUE(EVMS_MAJOR));
+	blk_dev[EVMS_MAJOR].queue = NULL;
+
+	devfs_unregister(evms_dir_devfs_handle);
+
+	unregister_blkdev(EVMS_MAJOR, EVMS_DIR_NAME);
+
+	mempool_destroy(evms_io_notify_pool);
+	kmem_cache_destroy(evms_io_notify_slab);
+}
+
+/**
+ * evms_cluster_init
+ *
+ * a placeholder for cluster enablement
+ **/
+void
+evms_cluster_init(int nodeid, int clusterid)
+{
+	/* dummy */
+	return;
+}
+
+EXPORT_SYMBOL(evms_cluster_init);
+
+/**
+ * evms_cluster_shutdown
+ *
+ * a placeholder for cluster enablement
+ **/
+int
+evms_cluster_shutdown(void)
+{
+	/* dummy */
+	return -1;
+}
+
+EXPORT_SYMBOL(evms_cluster_shutdown);
+
+/**
+ * evms_boot_info_level - parses the ascii evms info level parm on the loader's boot line
+ * @str:	the ascii info level to be parsed
+ *
+ * parses the ascii evms info level parm on the loader's boot line
+ *
+ * returns: 1 (always)
+ **/
+int __init
+evms_boot_info_level(char *str)
+{
+	int evms_boot_info_level = (int) simple_strtoul(str, NULL, 10);
+	if (evms_boot_info_level) {
+		evms_info_level = evms_boot_info_level;
+	}
+	return 1;
+}
+
+__setup("evms_info_level=", evms_boot_info_level);
+module_init(evms_init_module);
+module_exit(evms_exit_module);
+MODULE_LICENSE("GPL");
+

  reply	other threads:[~2002-10-10 20:12 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2002-10-10 19:30 [PATCH] EVMS core (0/9) Kevin Corry
2002-10-10 19:35 ` Kevin Corry [this message]
2002-10-10 19:35 ` [PATCH] EVMS core (2/9) services.c Kevin Corry
2002-10-10 19:35 ` [PATCH] EVMS core (3/9) discover.c Kevin Corry
2002-10-10 19:37 ` [PATCH] EVMS core (4/9) passthru.c Kevin Corry
2002-10-10 19:37 ` [PATCH] EVMS core (5/9) evms_core.h Kevin Corry
2002-10-10 19:37 ` [PATCH] EVMS core (6/9) evms.h Kevin Corry
2002-10-10 19:39 ` [PATCH] EVMS core (7/9) evms_ioctl.h Kevin Corry
2002-10-10 19:39 ` [PATCH] EVMS core (8/9) evms_biosplit.h Kevin Corry
2002-10-10 19:39 ` [PATCH] EVMS core (9/9) misc Kevin Corry

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=02101014350303.17770@boiler \
    --to=corryk@us.ibm.com \
    --cc=evms-devel@lists.sourceforge.net \
    --cc=linux-kernel@vger.kernel.org \
    --cc=torvalds@transmeta.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.