Part 3 of the EVMS core driver.
This file provides the code to coordinate volume
discovery among the EVMS plugins.
Kevin Corry
corryk@us.ibm.com
http://evms.sourceforge.net/
diff -Naur linux-2.5.41/drivers/evms/core/discover.c linux-2.5.41-evms/drivers/evms/core/discover.c
--- linux-2.5.41/drivers/evms/core/discover.c	Wed Dec 31 18:00:00 1969
+++ linux-2.5.41-evms/drivers/evms/core/discover.c	Thu Oct 10 13:14:27 2002
@@ -0,0 +1,1714 @@
+/*
+ *   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
+ */
+/*
+ * Core routines related to volume discovery and activation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/blk.h>
+#include <linux/root_dev.h>
+#include <linux/evms.h>
+#include <linux/evms_ioctl.h>
+#include "evms_core.h"
+
+/**
+ * evms_discover_logical_disk - invokes the discover code in each device manager plugin
+ * @disk_list:	ptr to logical disk (storage objects)
+ *
+ * Traverses the list of plugins, passing the disk_list to each
+ * device manager plugin type.
+ **/
+void
+evms_discover_logical_disks(struct list_head *disk_list)
+{
+	struct evms_plugin_header *plugin;
+	LOG_EXTRA("discovering logical disks...\n");
+	spin_lock(&plugin_lock);
+	list_for_each_entry(plugin, &plugin_head, headers) {
+		if (GetPluginType(plugin->id) == EVMS_DEVICE_MANAGER) {
+			spin_unlock(&plugin_lock);
+			DISCOVER(plugin, disk_list);
+			spin_lock(&plugin_lock);
+		}
+	}
+	spin_unlock(&plugin_lock);
+}
+
+/**
+ * evms_discover_segments - invokes the discover code in each segment manager plugin
+ * @discover_list:	ptr to discover list of storage objects
+ *
+ * Traverses the list of plugins, passing the discover list (list of objects
+ * to be probed) to each segment manager plugin type.
+ *
+ * Plugins claim storage objects by removing them from the discover list.
+ * Objects not claimed are left on the list. Newly created objects are put onto
+ * the list. The segment managers return a positive value indicating the number
+ * of object they put onto the list.
+ *
+ * Since segment managers can be stacked, when a segment manager puts a new
+ * object on the list it also returns a count to this function. If this
+ * function receives a positive count from segment manager, it must rerun
+ * through the entire list of segment managers again to allow each segment
+ * manager to probe the new object(s). Rerunning continues until no segment
+ * manager puts any new objects onto the list.
+ *
+ * Plugins with incomplete objects do not put them on the discover list when
+ * invoked through their discover entry point. However, when the plugin's
+ * end_discover entry point is called, they can then put partial volumes (in
+ * READ-ONLY mode) on to the list.
+ **/
+static void
+evms_discover_segments(struct list_head *discover_list)
+{
+	int rc, done;
+
+	struct evms_plugin_header *plugin;
+	LOG_EXTRA("discovering segments...\n");
+	do {
+		done = 1;
+		spin_lock(&plugin_lock);
+		list_for_each_entry(plugin, &plugin_head, headers) {
+			if (GetPluginType(plugin->id) == EVMS_SEGMENT_MANAGER) {
+				spin_unlock(&plugin_lock);
+				rc = DISCOVER(plugin, discover_list);
+				spin_lock(&plugin_lock);
+				if (rc > 0)
+					done = 0;
+			}
+		}
+		spin_unlock(&plugin_lock);
+	} while (done == 0);
+
+	spin_lock(&plugin_lock);
+	list_for_each_entry(plugin, &plugin_head, headers) {
+		if (GetPluginType(plugin->id) == EVMS_SEGMENT_MANAGER) {
+			if (plugin->fops->end_discover) {
+				spin_unlock(&plugin_lock);
+				END_DISCOVER(plugin, discover_list);
+				spin_lock(&plugin_lock);
+			}
+		}
+	}
+	spin_unlock(&plugin_lock);
+}
+
+/**
+ * evms_discover_regions - invokes the discover code in each region manager plugin
+ * @dicover_list:	ptr to discover list of storage objects
+ *
+ * Traverses the list of plugins, passing the discover list (list of objects
+ * to be probed) to each region manager plugin type.
+ *
+ * Plugins claim storage objects by removing them from the discover list.
+ * Objects not claimed are left on the list. Newly created objects are put onto
+ * the list. The region managers return a positive value indicating the number
+ * of object they put onto the list.
+ *
+ * Since region managers can be stacked, when a region manager puts a new object
+ * on the list it also returns a count to this function. If this function
+ * receives a positive count from region manager, it must rerun through the
+ * entire list of region managers again to allow each region manager to probe
+ * the new object(s). Rerunning continues until no region manager puts any new
+ * objects onto the list.
+ *
+ * Plugins with incomplete objects do not put them on the discover list when
+ * invoked through their discover entry point. However, when the plugin's
+ * end_discover entry point is called, they can then put partial volumes (in
+ * READ-ONLY mode) on to the list.
+ **/
+static void
+evms_discover_regions(struct list_head *discover_list)
+{
+	int rc, done;
+
+	struct evms_plugin_header *plugin;
+	LOG_EXTRA("discovering regions...\n");
+	do {
+		done = 1;
+		spin_lock(&plugin_lock);
+		list_for_each_entry(plugin, &plugin_head, headers) {
+			if (GetPluginType(plugin->id) == EVMS_REGION_MANAGER) {
+				spin_unlock(&plugin_lock);
+				rc = DISCOVER(plugin, discover_list);
+				spin_lock(&plugin_lock);
+				if (rc > 0)
+					done = 0;
+			}
+		}
+		spin_unlock(&plugin_lock);
+	} while (done == 0);
+
+	spin_lock(&plugin_lock);
+	list_for_each_entry(plugin, &plugin_head, headers) {
+		if (GetPluginType(plugin->id) == EVMS_REGION_MANAGER) {
+			if (plugin->fops->end_discover) {
+				spin_unlock(&plugin_lock);
+				END_DISCOVER(plugin, discover_list);
+				spin_lock(&plugin_lock);
+			}
+		}
+	}
+	spin_unlock(&plugin_lock);
+}
+
+/**
+ * le_feature_header_to_cpu - convert a feature header struct to native cpu format
+ * @fh:		feature header ptr
+ *
+ * Convert all the feature header fields into cpu native format from the
+ * on-disk Little Endian format. From this point forward all plugins can deal
+ * with feature headers natively.
+ **/
+void
+le_feature_header_to_cpu(struct evms_feature_header *fh)
+{
+	fh->signature = le32_to_cpup(&fh->signature);
+	fh->crc = le32_to_cpup(&fh->crc);
+	fh->version.major = le32_to_cpup(&fh->version.major);
+	fh->version.minor = le32_to_cpup(&fh->version.minor);
+	fh->version.patchlevel = le32_to_cpup(&fh->version.patchlevel);
+	fh->engine_version.major = le32_to_cpup(&fh->engine_version.major);
+	fh->engine_version.minor = le32_to_cpup(&fh->engine_version.minor);
+	fh->engine_version.patchlevel =
+	    le32_to_cpup(&fh->engine_version.patchlevel);
+	fh->flags = le32_to_cpup(&fh->flags);
+	fh->feature_id = le32_to_cpup(&fh->feature_id);
+	fh->sequence_number = le64_to_cpup(&fh->sequence_number);
+	fh->alignment_padding = le64_to_cpup(&fh->alignment_padding);
+	fh->feature_data1_start_lsn =
+	    le64_to_cpup(&fh->feature_data1_start_lsn);
+	fh->feature_data1_size = le64_to_cpup(&fh->feature_data1_size);
+	fh->feature_data2_start_lsn =
+	    le64_to_cpup(&fh->feature_data2_start_lsn);
+	fh->feature_data2_size = le64_to_cpup(&fh->feature_data2_size);
+	fh->volume_serial_number = le64_to_cpup(&fh->volume_serial_number);
+	fh->volume_system_id = le32_to_cpup(&fh->volume_system_id);
+	fh->object_depth = le32_to_cpup(&fh->object_depth);
+}
+
+/**
+ * edef_load_feature_header - load a feature header into memory
+ * @node:	storage object to feature header is read
+ *
+ * load and validate the feature header for a storage object. validation consists
+ * of verifying the signature and crc of the structure. since there is redundant (two)
+ * copies of the feature header, check them both, and select the most recent copy.
+ *
+ * returns: 0 = success
+ *          otherwise error code
+ **/
+static int
+edef_load_feature_header(struct evms_logical_node *node)
+{
+	int i, rc = 0, rc_array[2] = { 0, 0 };
+	ulong size_in_bytes;
+	u64 size_in_sectors, starting_sector = 0;
+	struct evms_feature_header *fh = NULL, *fh1 = NULL, *fh2 = NULL;
+	char *location_name = NULL;
+	struct evms_version version = {
+		EVMS_FEATURE_HEADER_MAJOR,
+		EVMS_FEATURE_HEADER_MINOR,
+		EVMS_FEATURE_HEADER_PATCHLEVEL
+	};
+
+	if (node->feature_header) {
+		return 0;
+	}
+	/* allocate buffers for feature headers */
+	size_in_sectors = evms_cs_size_in_vsectors(sizeof (*fh));
+	size_in_bytes = size_in_sectors << EVMS_VSECTOR_SIZE_SHIFT;
+	fh1 = kmalloc(size_in_bytes, GFP_KERNEL);
+	if (!fh1) {
+		return -ENOMEM;
+	}
+	fh2 = kmalloc(size_in_bytes, GFP_KERNEL);
+	if (!fh2) {
+		kfree(fh1);
+		return -ENOMEM;
+	}
+	for (i = 0; i < 2; i++) {
+		if (i == 0) {
+			starting_sector =
+			    node->total_vsectors - size_in_sectors;
+			fh = fh1;
+			location_name = EVMS_PRIMARY_STRING;
+		} else {
+			starting_sector--;
+			fh = fh2;
+			location_name = EVMS_SECONDARY_STRING;
+		}
+		/* read header into buffer */
+		rc = INIT_IO(node, 0, starting_sector, size_in_sectors, fh);
+		if (rc) {
+			LOG_ERROR("error(%d) probing for %s feature header "
+				  "(at "PFU64") on '%s'.\n", rc, location_name,
+				  starting_sector, node->name);
+			rc_array[i] = rc;
+			continue;
+		}
+		/* validate header signature */
+		if (cpu_to_le32(fh->signature) != EVMS_FEATURE_HEADER_SIGNATURE) {
+			rc_array[i] = -ENODATA;
+			continue;
+		}
+		/* validate header CRC */
+		if (fh->crc != EVMS_MAGIC_CRC) {
+			u32 org_crc, final_crc;
+			org_crc = cpu_to_le32(fh->crc);
+			fh->crc = 0;
+			final_crc =
+			    evms_cs_calculate_crc(EVMS_INITIAL_CRC, fh,
+						  sizeof (*fh));
+			if (final_crc != org_crc) {
+				LOG_ERROR("CRC mismatch error [stored(%x), "
+					  "computed(%x)] in %s feature header "
+					  "(at "PFU64") on '%s'.\n",
+					  org_crc, final_crc, location_name,
+					  starting_sector, node->name);
+				rc_array[i] = -EINVAL;
+				continue;
+			}
+		} else {
+			LOG_WARNING("CRC disabled in %s feature header "
+				    "(at "PFU64") on '%s'.\n", location_name,
+				    starting_sector, node->name);
+		}
+		/* convert the feature header from the
+		 * on-disk format (Little Endian) to
+		 * native cpu format.
+		 */
+		le_feature_header_to_cpu(fh);
+		/* verify the system data version */
+		rc = evms_cs_check_version(&version, &fh->version);
+		if (rc) {
+			LOG_ERROR("error: obsolete version(%d,%d,%d) in %s "
+				  "feature header on '%s'.\n",
+				  fh->version.major, fh->version.minor,
+				  fh->version.patchlevel, location_name,
+				  node->name);
+			rc_array[i] = rc;
+		}
+	}
+	/* analyze return codes from both copies */
+	/* getting same return code for both copies? */
+	if (rc_array[0] == rc_array[1]) {
+		rc = rc_array[0];
+		/* if no errors on both copies,
+		 * check the sequence numbers.
+		 * use the highest sequence number.
+		 */
+		if (!rc) {
+			/* compare sequence numbers */
+			if (fh1->sequence_number == fh2->sequence_number) {
+				fh = fh1;
+			} else {
+				LOG_WARNING("%s feature header sequence number"
+					    "("PFU64") mismatches %s feature "
+					    "header sequence number("PFU64") "
+					    "on '%s'!\n", EVMS_PRIMARY_STRING,
+					    fh1->sequence_number,
+					    EVMS_SECONDARY_STRING,
+					    fh2->sequence_number, node->name);
+				if (fh1->sequence_number > fh2->sequence_number) {
+					fh = fh1;
+					location_name = EVMS_PRIMARY_STRING;
+					/* indicate bad sequence number of secondary */
+					rc_array[1] = -1;
+				} else {
+					fh = fh2;
+					location_name = EVMS_SECONDARY_STRING;
+					/* indicate bad sequence number of primary */
+					rc_array[0] = -1;
+				}
+			}
+		}
+	} else			/* getting different return codes for each copy */
+	/* if either primary or secondary copy is
+	   * valid, use the valid copy.
+	 */ if ((rc_array[0] == 0) || (rc_array[1] == 0)) {
+		char *warn_name = NULL;
+
+		/* indicate success */
+		rc = 0;
+		/* set variables based on which copy is valid */
+		if (rc_array[0] == 0) {
+			/* use primary (rear) copy if its good */
+			fh = fh1;
+			location_name = EVMS_PRIMARY_STRING;
+			warn_name = EVMS_SECONDARY_STRING;
+		} else {
+			/* use secondary (front) copy if its good */
+			fh = fh2;
+			location_name = EVMS_SECONDARY_STRING;
+			warn_name = EVMS_PRIMARY_STRING;
+		}
+		/* warn the user about the invalid copy */
+		LOG_WARNING("warning: error(%d) probing/verifying the %s "
+			    "feature header on '%s'.\n",
+			    rc_array[0] + rc_array[1], warn_name, node->name);
+	} else
+		/* both copies had a different error,
+		 * and one was a fatal error, so
+		 * indicate fatal error.
+		 */
+	if ((rc_array[0] == -EINVAL) || (rc_array[1] == -EINVAL)) {
+		rc = -EINVAL;
+	}
+	/* on error, set fh to NULL */
+	if (rc) {
+		fh = NULL;
+	}
+	/* deallocate metadata buffers appropriately */
+	if (fh != fh2)
+		kfree(fh2);
+	if (fh != fh1)
+		kfree(fh1);
+	/* save validated feature header pointer */
+	if (!rc) {
+		node->feature_header = fh;
+		if (rc_array[0] != rc_array[1]) {
+			LOG_DETAILS("using %s feature header on '%s'.\n",
+				    location_name, node->name);
+		}
+	}
+	/* if no signature found, adjust return code */
+	if (rc == -ENODATA) {
+		rc = 0;
+		LOG_DEBUG("no feature header found on '%s'.\n", node->name);
+	}
+	return rc;
+}
+
+/**
+ * edef_find_first_features - probes potential EVMS bottom-most objects for EVMS metadata.
+ * @discover_list:	ptr to list of storage objects to probe
+ *
+ * probe this list of objects to see if any objects have EVMS metadata on them. this
+ * probe occurs after regions discover has been completed and represents the earlier
+ * point at which EVMS features could be found.
+ *
+ * if a storage object is found to have EVMS metadata, we allocate a volume info structure,
+ * which keeps track of the persistant EVMS volume data (Serial #, minor #, name). this
+ * volume info structure is then shared by all storage objects built off/from this storage
+ * object. 
+ * 
+ * EVMS also has metadata it uses to determine if a region/segment/disk is not to be
+ * exported. if we detect these flags here, we purge the storage object from memory. 
+ *
+ * returns: 0 = success
+ *	    otherwise error code
+ **/
+static int
+edef_find_first_features(struct list_head *discover_list)
+{
+	int rc;
+	struct evms_logical_node *node, *next_node, *tmp_node;
+
+	list_for_each_entry_safe(node, next_node, discover_list, discover) {
+		int found = 0;
+
+		/* check for duplicate pointers
+		 * search for the node in evms fbottom list
+		 */
+		list_for_each_entry(tmp_node, &evms_fbottom_list, fbottom) {
+			if (tmp_node == node) {
+				found = 1;
+				break;
+			}
+		}
+		/* already present? */
+		if (found) {
+			/* yes, already present */
+			LOG_DETAILS("deleting duplicate reference to '%s'.\n",
+				    node->name);
+			/* forgot this node by not adding back
+			 * onto the discover list.
+			 */
+			list_del_init(&node->discover);
+			continue;
+		}
+		/* load the feature header if present */
+		rc = edef_load_feature_header(node);
+		if (rc) {
+			LOG_ERROR("error(%d): reading feature header on '%s'\n",
+				  rc, node->name);
+			list_del_init(&node->discover);
+			DELETE(node);
+			continue;
+		}
+		if (node->feature_header) {
+			/* check for object flag */
+			if (node->feature_header->flags &
+			    EVMS_VOLUME_DATA_OBJECT) {
+				LOG_DEFAULT("object detected, deleting '%s'.\n",
+					    node->name);
+				list_del_init(&node->discover);
+				DELETE(node);
+				continue;
+			}
+			/* check for stop-data flag */
+			if (node->feature_header->flags & EVMS_VOLUME_DATA_STOP) {
+				LOG_DEFAULT("stop data detected, deleting "
+					    "'%s'.\n", node->name);
+				list_del_init(&node->discover);
+				DELETE(node);
+				continue;
+			}
+			/* we have a valid feature header.
+			   * initialize appropriate node fields
+			   * to indicate this.
+			 */
+			node->flags |= EVMS_VOLUME_FLAG;
+			node->iflags |= EVMS_FEATURE_BOTTOM;
+			node->volume_info
+			    = kmalloc(sizeof (struct evms_volume_info),
+				      GFP_KERNEL);
+			if (!node->volume_info) {
+				LOG_ERROR("error(%d): allocating volume info "
+					  "struct, deleting '%s'.\n",
+					  -ENOMEM, node->name);
+				list_del_init(&node->discover);
+				DELETE(node);
+				continue;
+			}
+			/* set up volume
+			   * info struct
+			 */
+			node->volume_info->
+			    volume_sn =
+			    node->feature_header->volume_serial_number;
+			node->volume_info->
+			    volume_minor =
+			    node->feature_header->volume_system_id;
+			strcpy(node->volume_info->
+			       volume_name, node->feature_header->volume_name);
+			/* register(add) node to the bottom feature list */
+			list_add(&node->fbottom, &evms_fbottom_list);
+		}
+	}
+	return 0;
+}
+
+/**
+ * edef_isolate_nodes_ty_type - move storage objects by type from one list to another
+ * @type:     		type of node to move (see defines below)
+ * @src_list:		source list to search
+ * @trg_list:		target list to put matching nodes
+ * @compare32:		32-bit comparison field
+ * @compare64:		64-bit comparison field
+ *
+ * moves storage object nodes by type from one list to another.
+ *
+ * returns: 0 = success
+ *	    otherwise error code
+ **/
+/**
+ * file @type defines
+ **/
+#define ISOLATE_ASSOCIATIVE_FEATURES		0
+#define ISOLATE_COMPATIBILITY_VOLUMES		1
+#define ISOLATE_EVMS_VOLUMES			2
+#define ISOLATE_EVMS_VOLUME_SERIAL_NUMBER	3
+#define ISOLATE_EVMS_NODES_BY_FEATURE_AND_DEPTH	4
+
+static int
+edef_isolate_nodes_by_type(unsigned int type,
+			   struct list_head *src_list,
+			   struct list_head *trg_list,
+			   u32 compare32, u64 compare64)
+{
+	struct evms_logical_node *node, *next_node;
+	int rc = 0, found_node;
+	struct evms_feature_header *fh = NULL;
+
+	list_for_each_entry_safe(node, next_node, src_list, discover) {
+		if (node->feature_header)
+			fh = node->feature_header;
+		found_node = 0;
+		switch (type) {
+		case ISOLATE_ASSOCIATIVE_FEATURES:
+			if (fh) {
+				if (GetPluginType(fh->feature_id) ==
+				    EVMS_ASSOCIATIVE_FEATURE) {
+					found_node = 1;
+				}
+			}
+			break;
+		case ISOLATE_COMPATIBILITY_VOLUMES:
+			if (!(node->flags & EVMS_VOLUME_FLAG)) {
+				found_node = 1;
+			}
+			break;
+		case ISOLATE_EVMS_VOLUMES:
+			if (node->flags & EVMS_VOLUME_FLAG) {
+				found_node = 1;
+			}
+			break;
+			/* EVMS volumes with same serial # */
+		case ISOLATE_EVMS_VOLUME_SERIAL_NUMBER:
+			if (node->volume_info->volume_sn == compare64) {
+				found_node = 1;
+			}
+			break;
+		case ISOLATE_EVMS_NODES_BY_FEATURE_AND_DEPTH:
+			if (fh) {
+				if (fh->object_depth == compare64) {
+					if (fh->feature_id == compare32) {
+						found_node = 1;
+					}
+				}
+			}
+			break;
+		}
+		if (found_node == 1) {
+			list_del_init(&node->discover);
+			list_add(&node->discover, trg_list);
+		}
+	}
+	return rc;
+}
+
+/**
+ * edef_apply_feature - apply a feature to a list of objects 
+ * @node:		object whose plugin's discover function will be invoked
+ * @volume_node_list:	list of potentially associated objects used during discover
+ *
+ * invoke the node's plugin's discover entry point and pass in the list of objects
+ * that can be processed by this plugin.
+ *
+ * returns: 0 = success
+ *	    otherwise error code
+ **/
+static int
+edef_apply_feature(struct evms_logical_node *node,
+		   struct list_head *volume_node_list)
+{
+	struct evms_plugin_header *plugin;
+	spin_lock(&plugin_lock);
+	list_for_each_entry(plugin, &plugin_head, headers) {
+		if (plugin->id == node->feature_header->feature_id) {
+			spin_unlock(&plugin_lock);
+			return DISCOVER(plugin, volume_node_list);
+		}
+	}
+	spin_unlock(&plugin_lock);
+	return -ENOPKG;
+}
+
+/**
+ * edef_get_feature_plugin_header - retrieves the header record for a plugin
+ * @id:	       	the feature or plugin ID
+ * @header:	the returned plugin header record
+ *
+ * retrieve the header record for the specified plugin.
+ *
+ * returns: 0 = success
+ *	    otherwise error code
+ **/
+static int
+edef_get_feature_plugin_header(u32 id, struct evms_plugin_header **header)
+{
+	struct evms_plugin_header *plugin;
+	spin_lock(&plugin_lock);
+	list_for_each_entry(plugin, &plugin_head, headers) {
+		if (plugin->id == id) {
+			spin_unlock(&plugin_lock);
+			*header = plugin;
+			return 0;
+		}
+	}
+	spin_unlock(&plugin_lock);
+	LOG_SERIOUS("no plugin loaded for feature id(0x%x)\n", id);
+	return -ENOPKG;
+}
+
+/**
+ * struct evms_volume_build_info - handy struct used during volume create time
+ * @node_count:			count of objects in this pass
+ * @feature_count:		count of unique plugins for the objects in this pass
+ * @associative_feature_count:	count of objects needing associations to continue
+ * @max_depth:			object with max volume depth on this pass
+ * @plugin:			plugin the max depth object requires to continue
+ * @feature_node_list:		list of nodes at max depth requiring @plugin
+ *
+ * handy structure used to track volume building info. updated and reused on each pass.
+ **/
+struct evms_volume_build_info {
+	int node_count;
+	int feature_count;
+	int associative_feature_count;
+	u64 max_depth;
+	struct evms_plugin_header *plugin;
+	struct list_head feature_node_list;
+};
+
+/**
+ * edef_evaluate_volume_node_list - performs one pass on the volume node list
+ * @volume_node_list:	list of nodes to built into a volume
+ * @vbi:		per pass volume build info
+ * @volume_complete:	status flag
+ *
+ *   does:
+ *	1) put all nodes from feature list back on volume list
+ *      2) loads the node's feature headers
+ *      3) counts the node list's entries
+ *      4) builds the feature node list
+ *	5) counts the feature headers for associative features
+ *	6) sets feature count to >1 if >1 features to be processed
+ *
+ * returns: 0 = success
+ *	    otherwise error code
+ */
+static int
+edef_evaluate_volume_node_list(struct list_head *volume_node_list,
+			       struct evms_volume_build_info *vbi,
+			       int volume_complete)
+{
+	int rc;
+	struct evms_logical_node *node;
+
+	vbi->node_count =
+	    vbi->feature_count =
+	    vbi->associative_feature_count = vbi->max_depth = 0;
+	vbi->plugin = NULL;
+
+	/* put all feature nodes back on the volume list */
+	rc = edef_isolate_nodes_by_type(ISOLATE_EVMS_VOLUMES,
+					&vbi->feature_node_list,
+					volume_node_list, 0, 0);
+	if (rc) {
+		return rc;
+	}
+	/* load all the feature headers */
+	if (!volume_complete) {
+		list_for_each_entry(node, volume_node_list, discover) {
+			rc = edef_load_feature_header(node);
+			if (rc) {
+				return rc;
+			}
+		}
+	}
+
+	/* find the 1st max depth object:
+	 *   record the depth
+	 *   record the plugin
+	 */
+	list_for_each_entry(node, volume_node_list, discover) {
+		struct evms_plugin_header *plugin;
+		struct evms_feature_header *fh = node->feature_header;
+
+		/* count the nodes */
+		vbi->node_count++;
+
+		/* no feature header found, continue to next node */
+		if (!fh) {
+			continue;
+		}
+		/* check the depth */
+		if (fh->object_depth > vbi->max_depth) {
+			/* record new max depth */
+			vbi->max_depth = fh->object_depth;
+			/* find the plugin header for this feature id */
+			rc = edef_get_feature_plugin_header(fh->feature_id,
+							    &plugin);
+			if (rc) {
+				return rc;
+			}
+			/* check for >1 plugins */
+			if (vbi->plugin != plugin) {
+				vbi->feature_count++;
+				vbi->plugin = plugin;
+			}
+		}
+		/* check for "associative" feature indicator */
+		if (GetPluginType(vbi->plugin->id) == EVMS_ASSOCIATIVE_FEATURE) {
+			vbi->associative_feature_count++;
+		}
+	}
+	/* build a list of max depth nodes for this feature */
+	if (vbi->max_depth) {
+		rc = edef_isolate_nodes_by_type
+		    (ISOLATE_EVMS_NODES_BY_FEATURE_AND_DEPTH, volume_node_list,
+		     &vbi->feature_node_list, vbi->plugin->id, vbi->max_depth);
+		if (rc) {
+			return rc;
+		}
+		if (!vbi->plugin) {
+			return -ENODATA;
+		}
+		if (list_empty(&vbi->feature_node_list)) {
+			return -ENODATA;
+		}
+	}
+	return rc;
+}
+
+/**
+ * edef_check_feature_conditions
+ * @vbi:	volume build info
+ *
+ * This routine verifies the state of volume based on the features
+ * headers and nodes in the current @vbi.
+ */
+static int
+edef_check_feature_conditions(struct evms_volume_build_info *vbi)
+{
+	if (vbi->associative_feature_count) {
+		if (vbi->node_count > 1) {
+			LOG_ERROR("associative ERROR: > 1 nodes(%d) remaining "
+				  "to be processed!\n", vbi->node_count);
+			return -EVMS_VOLUME_FATAL_ERROR;
+		}
+		if (vbi->max_depth != 1) {
+			LOG_ERROR("associative ERROR: associative feature "
+				  "found at node depth("PFU64") != 1!\n",
+				  vbi->max_depth);
+			return -EVMS_VOLUME_FATAL_ERROR;
+		}
+		return -EVMS_ASSOCIATIVE_FEATURE;
+	}
+	if (!vbi->max_depth) {
+		if (vbi->node_count > 1) {
+			LOG_ERROR("max depth ERROR: > 1 nodes(%d) remaining "
+				  "to be processed!\n", vbi->node_count);
+			return -EVMS_VOLUME_FATAL_ERROR;
+		}
+	}
+	if (vbi->max_depth == 1) {
+		if (vbi->feature_count > 1) {
+			LOG_ERROR("max depth 1 ERROR: > 1 features remaining "
+				  "to be processed!\n");
+			return -EVMS_VOLUME_FATAL_ERROR;
+		}
+	}
+	return 0;
+}
+
+/** edef_apply_features
+ * @volume_node_list:	all the objects to be built into a volume
+ *
+ * This routine applies none, one, or more features to an EVMS
+ * volume. The features are applied and verified recursively until the
+ * entire volume has been constructed. Fatal errors result in
+ * all nodes in the volume discovery list being deleted.
+ *
+ * returns: 0 = success
+ *	    otherwise error code
+ */
+static int
+edef_apply_features(struct list_head *volume_node_list)
+{
+	int rc, done, top_feature_applying;
+	struct evms_volume_build_info vbi;
+
+	INIT_LIST_HEAD(&vbi.feature_node_list);
+	rc = edef_evaluate_volume_node_list(volume_node_list, &vbi, 0);
+
+	/* ensure we don't go into the next loop
+	 * without having a target plugin to
+	 * pass control to.
+	 */
+	if (!rc) {
+		if (!vbi.plugin) {
+			rc = -ENODATA;
+		}
+	}
+
+	/* this loop should ONLY get used when
+	 * there are features to process.
+	 */
+	done = (rc) ? 1 : 0;
+	while (!done) {
+		rc = edef_check_feature_conditions(&vbi);
+		if (rc)
+			break;
+		top_feature_applying = (vbi.max_depth == 1) ? 1 : 0;
+		rc = vbi.plugin->fops->discover(&vbi.feature_node_list);
+		if (!rc) {
+			rc = edef_evaluate_volume_node_list(volume_node_list,
+							    &vbi,
+							    top_feature_applying);
+			if (top_feature_applying == 1) {
+				if (vbi.node_count > 1) {
+					rc = -EVMS_VOLUME_FATAL_ERROR;
+					LOG_ERROR("ERROR: detected > 1 node at "
+						  "volume completion!\n");
+				}
+				done = 1;
+			} else {
+				if (!vbi.plugin) {
+					rc = -EVMS_VOLUME_FATAL_ERROR;
+					LOG_ERROR("ERROR: depth("PFU64"): "
+						  "expected another feature!\n",
+						  vbi.max_depth);
+					done = 1;
+				}
+			}
+		} else {	/* rc != 0 */
+			rc = -EVMS_VOLUME_FATAL_ERROR;
+			done = 1;
+		}
+	}
+	if (rc) {
+		/* put all feature nodes back on the volume list */
+		if (edef_isolate_nodes_by_type(ISOLATE_EVMS_VOLUMES,
+					       &vbi.feature_node_list,
+					       volume_node_list, 0, 0)) {
+			BUG();
+		}
+	}
+	return rc;
+}
+
+/**
+ * edef_delete_node - generic delete an object from a volume node list
+ * @node_list:		the current object list
+ * @node:		the object to be deleted
+ * @return_code:	the error code
+ * @log_text:		logging text
+ *
+ * convenience function to delete an object from a volume list. this generates
+ * the appropriate syslog messages and sets the return code.
+ *
+ * returns: 0 = success
+ *	    otherwise error code
+ **/
+static int
+edef_delete_node(struct list_head *node_list,
+		 struct evms_logical_node *node, int return_code,
+		 char *log_text)
+{
+	int rc;
+
+	if (!list_empty(&node->discover)) {
+		list_del_init(&node->discover);
+		LOG_ERROR("%s error(%d): deleting volume(%s), node(%s)\n",
+			  log_text, return_code,
+			  node->volume_info->volume_name, node->name);
+		rc = DELETE(node);
+		if (rc) {
+			LOG_ERROR("error(%d) while deleting node(%s)\n",
+				  rc, node->name);
+		}
+	} else {
+		LOG_WARNING("%s error(%d): node gone, assumed deleted by "
+			    "plugin.\n", log_text, return_code);
+		/* plugin must have cleaned up the node.
+		 * So just reset the return code and leave.
+		 */
+		rc = 0;
+	}
+
+	return rc;
+}
+
+/**
+ * edef_process_evms_volumes - build an EVMS volume from storage objects
+ * @discover_list:		list of all objects being discovered
+ * @associative_feature_list:	returned list of objects required associative features
+ *
+ * groups all evms objects with the same serial number onto a volume list and sends
+ * that list into edef_apply_features. this process is repeated unto all evms volume
+ * serial number groups have been processed. objects representing complete volumes
+ * are put back on the discover list and objects requiring associative processing
+ * are put on an associative feature list.
+ *
+ * returns: 0 = success
+ *	    otherwise error code
+ **/
+static int
+edef_process_evms_volumes(struct list_head *discover_list,
+			  struct list_head *associative_feature_list)
+{
+	int rc = 0;
+	struct list_head evms_volumes_list, volume_node_list;
+	struct evms_logical_node *node = NULL;
+	u64 volume_sn;
+
+	INIT_LIST_HEAD(&evms_volumes_list);
+	/* put all EVMS volumes on their own list */
+	rc = edef_isolate_nodes_by_type(ISOLATE_EVMS_VOLUMES,
+					discover_list,
+					&evms_volumes_list, 0, 0);
+
+	/* apply features to each EVMS volume */
+	/* one volume at a time on each pass  */
+	while (!list_empty(&evms_volumes_list)) {
+		node = list_entry(evms_volumes_list.next, struct evms_logical_node, discover);
+		/* put all nodes for one EVMS volume on separate list */
+		INIT_LIST_HEAD(&volume_node_list);
+		volume_sn = node->volume_info->volume_sn;
+		rc = edef_isolate_nodes_by_type
+		    (ISOLATE_EVMS_VOLUME_SERIAL_NUMBER, &evms_volumes_list,
+		     &volume_node_list, 0, volume_sn);
+		if (rc) {
+			break;
+		}
+		/* go apply all the volume features now */
+		rc = edef_apply_features(&volume_node_list);
+		switch (rc) {
+		case 0:	/* SUCCESS */
+			/* remove volume just processed */
+			node = list_entry(volume_node_list.next, struct evms_logical_node, discover);
+			list_del_init(&node->discover);
+			/* put volume on global list */
+			list_add_tail(&node->discover, discover_list);
+			break;
+		case -EVMS_ASSOCIATIVE_FEATURE:
+			/* put all "associative" features on their own list */
+			rc = edef_isolate_nodes_by_type
+			    (ISOLATE_ASSOCIATIVE_FEATURES, &volume_node_list,
+			     associative_feature_list, 0, 0);
+			break;
+		default:	/* FATAL ERROR */
+			/* delete each node remaining in the list */
+			if (!list_empty(&volume_node_list)) {
+				node = list_entry(volume_node_list.next, struct evms_logical_node, discover);
+				LOG_ERROR("encountered fatal error building "
+					  "volume '%s'\n",
+					  node->volume_info->volume_name);
+			}
+			while (!list_empty(&volume_node_list)) {
+				node = list_entry(volume_node_list.next, struct evms_logical_node, discover);
+				edef_delete_node(&volume_node_list,
+						 node, rc, "EVMS feature");
+			}
+			rc = 0;
+			break;
+		}
+		if (rc) {
+			break;
+		}
+	}
+	return rc;
+}
+
+/**
+ * edef_process_associative_volumes - find the associative feature for the objects in this list
+ * @associative_feature_list:	list of objects requiring associative features
+ * @discover_list:		list to put completed volumes object back onto
+ * 
+ * find and apply the appropriate associative feature for the objects on the
+ * associative feature list. if successfully applied, put the resulting completed
+ * volume objects on to the discover list. error result in the volume objects
+ * being deleted.
+ * 
+ * returns: 0 = success
+ *	    otherwise error code
+ **/
+static int
+edef_process_associative_volumes(struct list_head *associative_feature_list,
+				 struct list_head *discover_list)
+{
+	int rc = 0;
+	struct evms_logical_node *node, *next_node;
+
+	list_for_each_entry_safe(node, next_node, associative_feature_list, discover) {
+		/* remove this node from associative feature list */
+		list_del_init(&node->discover);
+		rc = edef_load_feature_header(node);
+		if (rc) {
+			edef_delete_node(discover_list, node, rc,
+					 "Associative Feature");
+			continue;
+		}
+		/* put volume on global list */
+		list_add_tail(&node->discover, discover_list);
+		rc = edef_apply_feature(node, discover_list);
+	}
+	return rc;
+}
+
+/**
+ * edef_check_for_incomplete_volumes
+ * @discover_list:	list of post discovery time volume objects
+ *
+ * check to see if any incomplete volumes are left around if so, delete them.
+ * complete volumes should not have feature_headers hanging off them, if we
+ * find any, we know the volume is incomplete.
+ **/
+static void
+edef_check_for_incomplete_volumes(struct list_head *discover_list)
+{
+	struct evms_logical_node *next_node, *node;
+
+	list_for_each_entry_safe(node, next_node, discover_list, discover) {
+		if (node->feature_header) {
+			edef_delete_node(discover_list, node, 0,
+					 "Unexpected feature header");
+		}
+	}
+}
+
+/**
+ * evms_discover_evms_features
+ * @discover_list:	list of objects to discover evms features on
+ *
+ * find EVMS features for nodes on the discover list
+ *
+ * returns: 0 = success
+ *	    otherwise error code
+ */
+static int
+evms_discover_evms_features(struct list_head *discover_list)
+{
+	struct list_head associative_feature_list;
+	int rc;
+
+	LOG_EXTRA("discovering evms volume features...\n");
+
+	INIT_LIST_HEAD(&associative_feature_list);
+	rc = edef_find_first_features(discover_list);
+	if (rc) {
+		return rc;
+	}
+	rc = edef_process_evms_volumes(discover_list,
+				       &associative_feature_list);
+	if (rc) {
+		return rc;
+	}
+	rc = edef_process_associative_volumes(&associative_feature_list,
+					      discover_list);
+	if (rc) {
+		return rc;
+	}
+	edef_check_for_incomplete_volumes(discover_list);
+	return 0;
+}
+
+/**
+ * eelv_geninit - initialize a gendisk entry for a volume
+ * @volume:	ptr to new volume
+ * @minor:	minor value of new volume
+ *
+ * This routine initializes a gendisk entry for the specified volume.
+ **/
+static void
+eelv_geninit(struct evms_logical_volume *lv)
+{
+	struct gendisk *gd = lv->gd;
+
+	if (!gd) {
+		gd = alloc_disk();
+		BUG_ON(!gd);
+		lv->gd = gd;
+	}
+
+	gd->major = EVMS_MAJOR;
+	gd->first_minor = lv->minor;
+	snprintf(gd->disk_name, 16, "%s%d", EVMS_DIR_NAME, lv->minor);
+	gd->fops = &evms_fops;
+	set_capacity(gd, lv->node->total_vsectors);
+
+	/* set removable flags as needed */
+	gd->flags = 0;
+	if (lv->flags & EVMS_DEVICE_REMOVABLE) {
+		gd->flags |= GENHD_FL_REMOVABLE;
+	}
+
+	gd->de = devfs_register(evms_dir_devfs_handle,
+				lv->name, DEVFS_FL_DEFAULT,
+				EVMS_MAJOR, lv->minor,
+				S_IFBLK | S_IRUGO | S_IWUGO,
+				&evms_fops, NULL);
+
+	add_disk(gd);
+}
+
+/**
+ * add_logical_volume - adds a newly created logical volume to our list
+ * @lv:		logical volume to be added.
+ *
+ * adds a newly created logical volume to our list.
+ * this function keeps the list ordered by minor
+ * number from low to high.
+ **/
+static void
+add_logical_volume(struct evms_logical_volume *lv)
+{
+	if (list_empty(&evms_logical_volumes)) {
+		list_add(&lv->volumes, &evms_logical_volumes);
+	} else {
+		struct evms_logical_volume *tmpvol;
+		list_for_each_entry(tmpvol, &evms_logical_volumes, volumes) {
+			BUG_ON(tmpvol->minor == lv->minor);
+			if (tmpvol->minor > lv->minor) {
+				break;
+			}
+		}
+		list_add(&lv->volumes, tmpvol->volumes.prev);
+	}
+}
+
+/**
+ * evms_do_request_fn
+ * @q:		request queue
+ **/
+static void
+evms_do_request_fn(request_queue_t * q)
+{
+	LOG_WARNING("This function should not be called.\n");
+}
+
+/**
+ * eelv_assign_volume_minor - assigns a minor number to a volume
+ * @node: 	volume object to process
+ * @minor:	minor device number to assign
+ *
+ * This routine assigns a specific minor number to a volume. It
+ * also performs the remaining steps to make this volume visible
+ * and usable to the kernel.
+ **/
+static void
+eelv_assign_volume_minor(struct evms_logical_node *node, int minor,
+			 struct evms_logical_volume *vol)
+{
+	struct evms_logical_volume *lv = vol;
+
+	if (!lv) {
+		lv = kmalloc(sizeof(struct evms_logical_volume), GFP_KERNEL);
+		BUG_ON(!lv);
+		memset(lv, 0, sizeof(struct evms_logical_volume));
+		lv->requests_in_progress = (atomic_t) ATOMIC_INIT(0);
+	}
+	lv->node = node;
+	lv->name = kmalloc(strlen(EVMS_GET_NODE_NAME(node)) + 1, GFP_KERNEL);
+	BUG_ON(!lv->name);
+	strcpy(lv->name, EVMS_GET_NODE_NAME(node));
+	lv->minor = minor;
+	lv->flags = node->flags;
+	if (lv->flags & EVMS_VOLUME_READ_ONLY) {
+		set_device_ro(mk_kdev(EVMS_MAJOR, minor), 1);
+	}
+
+	/* round volume size down to next hardsector */
+	node->total_vsectors &=
+	    ~((node->hardsector_size >> EVMS_VSECTOR_SIZE_SHIFT) - 1);
+
+	eelv_geninit(lv);
+
+	/* set up the request queue for this volume */
+	lv->requests_in_progress = (atomic_t) ATOMIC_INIT(0);
+	lv->request_lock = SPIN_LOCK_UNLOCKED;
+	if (!vol) {
+		init_waitqueue_head(&lv->quiesce_wait_queue);
+		init_waitqueue_head(&lv->request_wait_queue);
+		blk_init_queue(&lv->request_queue,
+			       evms_do_request_fn,
+			       &lv->request_lock);
+		blk_queue_make_request(&lv->request_queue,
+				       evms_make_request_fn);
+		add_logical_volume(lv);
+	}
+	evms_volumes++;
+	LOG_DEFAULT("Exporting EVMS Volume(%u,%u) from \"%s%s\".\n",
+		    EVMS_MAJOR, minor, EVMS_DIR_NAME "/", lv->name);
+}
+
+/**
+ * eelv_check_for_duplicity
+ * @discover_list: 	list of potential volume objects to export
+ *
+ * This routine compares the serial number in the top most node
+ * in the volume to the list of currently exported volumes. If
+ * this volumes serial number is found in the list then we know
+ * this volume is a duplicate and it is then delete.
+ **/
+static void
+eelv_check_for_duplicity(struct list_head *discover_list)
+{
+	struct evms_logical_node *next_node, *node;
+
+	list_for_each_entry_safe(node, next_node, discover_list, discover) {
+		struct evms_logical_volume *lv = NULL;
+		int is_dup = 0;
+		while ((lv = find_next_volume(lv))) {
+			char *type_ptr = NULL;
+			/* only check exported volumes */
+			if (!lv->node) {
+				continue;
+			}
+			/* check for duplicate pointer */
+			if (node == lv->node) {
+				is_dup = 1;
+				type_ptr = "pointer";
+				/* check for duplicate node */
+			} else if (!strcmp(node->name, lv->node->name)) {
+				is_dup = 1;
+				type_ptr = "node";
+			}
+			if (is_dup == 1) {
+				LOG_DETAILS("deleting duplicate %s to EVMS "
+					    "volume(%u,%u,%s)...\n",
+					    type_ptr, EVMS_MAJOR, lv->minor,
+					    EVMS_GET_NODE_NAME(node));
+				list_del_init(&node->discover);
+				/* forget duplicate */
+				break;
+			}
+		}
+	}
+}
+
+/**
+ * eelv_reassign_soft_deleted_volume_minors
+ * @discover_list: 	list of volume objects to export
+ *
+ * This routine reassigns previous minor numbers to rediscovered "soft"
+ * deleted volumes.
+ **/
+static void
+eelv_reassign_soft_deleted_volume_minors(struct list_head *discover_list)
+{
+	struct evms_logical_node *next_node, *node;
+
+	list_for_each_entry_safe(node, next_node, discover_list, discover) {
+		struct evms_logical_volume *lv = NULL;
+		while ((lv = find_next_volume(lv))) {
+			/* only check soft deleted volumes:
+			 *  they have a non-NULL name.
+			 */
+			if (!(lv->flags & EVMS_VOLUME_SOFT_DELETED)) {
+				continue;
+			}
+			if (strcmp(EVMS_GET_NODE_NAME(node), lv->name)) {
+				continue;
+			}
+			/* reassign requested minor */
+			list_del_init(&node->discover);
+			LOG_DEFAULT("Re");
+			/* free the previously used name */
+			kfree(lv->name);
+			lv->name = NULL;
+			/* clear the EVMS_VOLUME_SOFT_DELETED flag */
+			lv->flags = 0;
+			eelv_assign_volume_minor(node, lv->minor, lv);
+			break;
+		}
+	}
+}
+
+/**
+ * eelv_assign_evms_volume_minors
+ * @discover_list: 	list of volume objects to export
+ *
+ * This routine assigns minor numbers to new evms volumes. If
+ * the specified minor is already in use, the requested minor
+ * is set to 0, and will be assigned next available along with
+ * any remaining volumes at the end of evms_export_logical_volumes.
+ **/
+static void
+eelv_assign_evms_volume_minors(struct list_head *discover_list)
+{
+	struct evms_logical_node *next_node, *node, *lv_node;
+	unsigned int requested_minor, lv_flags;
+
+	list_for_each_entry_safe(node, next_node, discover_list, discover) {
+		/* only process evms volumes */
+		if (!(node->flags & EVMS_VOLUME_FLAG)) {
+			continue;
+		}
+		requested_minor = node->volume_info->volume_minor;
+		/* is there a requested minor? */
+		if (!requested_minor) {
+			continue;
+		}
+		/* check range of requested minor */
+		if (requested_minor >= MAX_EVMS_VOLUMES) {
+			lv_node = node;
+			lv_flags = 0;
+		} else {
+			struct evms_logical_volume *lv;
+			lv_node = NULL;
+			lv_flags = 0;
+			lv = lookup_volume(requested_minor);
+			if (lv) {
+				lv_node = lv->node;
+				lv_flags = lv->flags;
+			}
+		}
+		if (lv_node || (lv_flags & EVMS_VOLUME_SOFT_DELETED)) {
+			LOG_WARNING("EVMS volume(%s) requesting invalid/in-use "
+				    "minor(%d), assigning next available!\n",
+				    node->volume_info->volume_name,
+				    requested_minor);
+			/*
+			 * requested minor is already
+			 * in use, defer assignment
+			 * until later.
+			 */
+			node->volume_info->volume_minor = 0;
+		} else {
+			/* assign requested minor */
+			list_del_init(&node->discover);
+			eelv_assign_volume_minor(node, requested_minor, NULL);
+		}
+	}
+}
+
+/**
+ * find_unused_minor - finds the first unused minor based on the specified direction
+ * @dir:	direction of search (0 = forward, 1 = backward)
+ *
+ * Searches for the first available minor number in the specified search
+ * direction.
+ *
+ * Returns the first available minor number or 0. 0 is reserved for the block
+ * device itself and is therefore never usable for a logical volume.
+ **/
+static int
+find_unused_minor(int dir)
+{
+	int i;
+	if (!dir) {
+		for (i = 1; i < MAX_EVMS_VOLUMES; i++) {
+			if (!lookup_volume(i)) {
+				return i;
+			}
+		}
+	} else {
+		for (i = MAX_EVMS_VOLUMES - 1; i; i--) {
+			if (!lookup_volume(i)) {
+				return i;
+			}
+		}
+	}
+	return 0;
+}
+
+/**
+ * eelv_assign_remaining_evms_volume_minors
+ * @discover_list: 	list of volume objects to export
+ *
+ * This routine assigns minor numbers to new evms volumes that
+ * have no/conflicting minor assignments. This function will
+ * search from high(255) minor values down, for the first available
+ * minor. Searching high to low minimizes the possibility of
+ * conflicting evms volumes causing "compatibility" minor
+ * assignments to shift from expected assignments.
+ */
+static void
+eelv_assign_remaining_evms_volume_minors(struct list_head *discover_list)
+{
+	struct evms_logical_node *next_node, *node;
+	int minor;
+
+	list_for_each_entry_safe(node, next_node, discover_list, discover) {
+		/* only process evms volumes */
+		/* all remaining evms volumes should now
+		 * have a minor value of 0, meaning they
+		 * had no minor assignment, or their minor
+		 * assignment conflicted with an existing
+		 * minor assignment.
+		 */
+		if (!(node->flags & EVMS_VOLUME_FLAG)) {
+			continue;
+		}
+		list_del_init(&node->discover);
+		/* find next available minor number */
+		minor = find_unused_minor(1);
+		/* check range of assigned minor */
+		if (!minor) {
+			LOG_CRITICAL("no more minor numbers available for "
+				     "evms volumes!!!!\n");
+			DELETE(node);
+		} else {
+			/* assign requested minor */
+			eelv_assign_volume_minor(node, minor, NULL);
+		}
+	}
+}
+
+/**
+ * eelv_assign_remaining_volume_minors
+ * @discover_list: 	list of volume objects to export
+ *
+ * This routine assigns minor numbers to all remaining unassigned
+ * volumes. Minor numbers are assigned on an first availability
+ * basis. The first free minor number is used in the assignment.
+ **/
+static void
+eelv_assign_remaining_volume_minors(struct list_head *discover_list)
+{
+	struct evms_logical_node *node, *next_node;
+	int minor;
+
+	list_for_each_entry_safe(node, next_node, discover_list, discover) {
+		list_del_init(&node->discover);
+		/* find next available minor number */
+		minor = find_unused_minor(0);
+		if (!minor) {
+			LOG_CRITICAL("no more minor numbers available for "
+				     "compatibility volumes!!!!\n");
+			DELETE(node);
+		} else {
+			/* assign minor */
+			eelv_assign_volume_minor(node, minor, NULL);
+		}
+	}
+}
+
+/**
+ * eelv_check_for_unreassign_soft_deleted_volume
+ * @discover_list: 	list of volume objects to export
+ *
+ * This routine reports any "soft deleted" volumes that were not
+ * found after a rediscovery.
+ **/
+static void
+eelv_check_for_unreassign_soft_deleted_volume(void)
+{
+	struct evms_logical_volume *lv, *next_lv;
+
+	next_lv = NULL;
+	while ((lv = find_next_volume_safe(&next_lv))) {
+		/* only check soft deleted volumes:
+		 *  they have a NULL node ptr &
+		 *  they have a non-NULL name.
+		 */
+		if (!(lv->flags & EVMS_VOLUME_SOFT_DELETED)) {
+			continue;
+		}
+		if (is_busy(mk_kdev(EVMS_MAJOR, lv->minor))) {
+			lv->flags |= EVMS_VOLUME_CORRUPT;
+		}
+		LOG_ERROR("error: rediscovery failed to find %smounted "
+			  "'soft deleted' volume(%u,%u,%s)...\n",
+			  ((lv->flags & EVMS_VOLUME_CORRUPT) ? "" : "un"),
+			  EVMS_MAJOR, lv->minor, lv->name);
+		if (lv->flags & EVMS_VOLUME_CORRUPT) {
+			LOG_ERROR("    flagging volume(%u:%u,%s) as CORRUPT!\n",
+				  EVMS_MAJOR, lv->minor, lv->name);
+		} else {
+			LOG_ERROR("    releasing minor(%d) used by "
+				  "volume(%s)!\n", lv->minor, lv->name);
+			/* clear logical volume structure
+			 * for this volume so it may be
+			 * reused.
+			 */
+			blk_cleanup_queue(&lv->request_queue);
+			kfree(lv->name);
+			list_del_init(&lv->volumes);
+			kfree(lv);
+		}
+	}
+}
+
+/**
+ * eelv_unquiesce_volumes - unquiesces all volumes after a rediscover operation
+ *
+ * unquiesces all volumes after a rediscover operation.
+ **/
+static void
+eelv_unquiesce_volumes(void)
+{
+	struct evms_logical_volume *lv = NULL;
+	/* check each volume entry */
+	while((lv = find_next_volume(lv))) {
+		/* is this volume "quiesced" ? */
+		if (!lv->quiesced) {
+			continue;
+		}
+		if (!lv->node) {
+			continue;
+		}
+		/* "unquiesce" it */
+		evms_quiesce_volume(lv, 0, lv->minor, 0);
+	}
+}
+
+/**
+ * evms_export_logical_volumes
+ *
+ * This function is called from evms_discover_volumes. It
+ * check for duplicate volumes, assigns minor values to evms
+ * volumes, and assigns minor values to the remaining volumes.
+ * In addition to assigning minor values to each volume this
+ * function also completes the final steps necessary to allow
+ * the volumes to be using by the operating system.
+ **/
+static void
+evms_export_logical_volumes(struct list_head *discover_list)
+{
+	LOG_EXTRA("exporting EVMS logical volumes...\n");
+
+	eelv_check_for_duplicity(discover_list);
+	eelv_reassign_soft_deleted_volume_minors(discover_list);
+	eelv_assign_evms_volume_minors(discover_list);
+	eelv_assign_remaining_evms_volume_minors(discover_list);
+	eelv_assign_remaining_volume_minors(discover_list);
+	eelv_check_for_unreassign_soft_deleted_volume();
+	eelv_unquiesce_volumes();
+}
+
+/**
+ * edv_populate_discover_list - initially populates the discover list with disk objects
+ * @src_list:		source list for disk objects
+ * @trg_list:		target list for disk objects
+ * @discover_parms:	rediscover ioctl packet
+ *
+ * based on the presence or the contents of the rediscover packet, the discover list
+ * is populated with disk objects. if the @discover_parms == NULL, all disk objects
+ * will be copied to the discover list. if the @discover_parms points to a rediscover
+ * packet, the disks matching the disk handles from in the ioctl packet will be copied
+ * to the discover list.
+ *
+ **/
+static void
+edv_populate_discover_list(struct list_head *src_list,
+			   struct list_head *trg_list,
+			   struct evms_rediscover_pkt *discover_parms)
+{
+	int i, use_all_disks = 0;
+	struct evms_logical_node *node;
+
+	/* if no discover parameters are specified */
+	/* copy ALL the disk nodes into the        */
+	/* discovery list.                         */
+	if ((discover_parms == NULL) ||
+	    (discover_parms->drive_count == REDISCOVER_ALL_DEVICES))
+		use_all_disks = 1;
+
+	/* copy the disk nodes specified in the */
+	/* discover_parms over to a discover list */
+
+	list_for_each_entry(node, &evms_device_list, device) {
+		int move_node;
+		move_node = use_all_disks;
+		if (move_node == 0)
+			/* check the rediscovery array */
+			for (i = 0; i < discover_parms->drive_count; i++) {
+				struct evms_logical_node *disk_node = 
+				    DEV_HANDLE_TO_NODE(discover_parms->
+						       drive_array[i]);
+				if (disk_node == node) {
+					move_node = 1;
+					break;
+				}
+			}
+		/* check to see if we want this node */
+		if (move_node == 1) {
+			if (list_empty(&node->discover)) {
+				list_add_tail(&node->discover, trg_list);
+			}
+		}
+	}
+}
+
+/**
+ * evms_discover_volumes - probe the disks to discover volumes
+ * @discover_parms:	ptr to a rediscover ioctl packet
+ *
+ * perform a rediscover operation. the presence or contents of a rediscover packet
+ * will dictate the scope of the operation.
+ *
+ * returns: 0 = success
+ *	    otherwise error code
+ **/
+int
+evms_discover_volumes(struct evms_rediscover_pkt *discover_parms)
+{
+	struct list_head discover_list;
+
+	INIT_LIST_HEAD(&discover_list);
+	evms_discover_logical_disks(&discover_list);
+	if (!list_empty(&evms_device_list)) {
+		/* move the appropriate disk nodes, based on */
+		/* on the discover parameters, onto the      */
+		/* discover list for the partition managers  */
+		/* to process                                */
+		edv_populate_discover_list(&evms_device_list,
+					   &discover_list, discover_parms);
+	}
+	if (!list_empty(&discover_list)) {
+		evms_discover_segments(&discover_list);
+	}
+	if (!list_empty(&discover_list)) {
+		evms_discover_regions(&discover_list);
+	}
+	if (!list_empty(&discover_list)) {
+		evms_discover_evms_features(&discover_list);
+	}
+	if (!list_empty(&discover_list)) {
+		evms_export_logical_volumes(&discover_list);
+		evms_cs_signal_event(EVMS_EVENT_END_OF_DISCOVERY);
+	}
+	BUG_ON(!list_empty(&discover_list));
+	return 0;
+}
+
+/**
+ * find_root_fs_dev
+ *
+ * If "root=/dev/evms/???" was specified on the kernel command line, and devfs
+ * is not enabled, we need to determine the appropriate minor number for the
+ * specified volume for the root fs.
+ *
+ * This function will never get called if EVMS is built as a module, and
+ * adding the #ifndef MODULE condition prevents having to add an
+ * EXPORT_SYMBOL() somewhere for get_root_device_name.
+ */
+static void
+find_root_fs_dev(void)
+{
+#ifndef MODULE
+	struct evms_logical_volume *lv;
+	char root_name[64] = { 0 };
+	char *name;
+
+	//get_root_device_name(root_name);
+
+	if (!strncmp(root_name, EVMS_DIR_NAME "/", strlen(EVMS_DIR_NAME) + 1)) {
+		name = &root_name[strlen(EVMS_DIR_NAME) + 1];
+		
+		lv = NULL;
+		while ((lv = find_next_volume(lv))) {
+			if (lv->name &&
+			    !strncmp(name, lv->name, strlen(lv->name))) {
+				ROOT_DEV = kdev_val(mk_kdev(EVMS_MAJOR, lv->minor));
+				return;
+			}
+		}
+	}
+#endif
+}
+
+/**
+ * evms_init_discover
+ *
+ * If EVMS is statically built into the kernel, this function will be called
+ * to perform an initial volume discovery.
+ **/
+int __init
+evms_init_discover(void)
+{
+	/* Go find volumes */
+	evms_discover_volumes(NULL);
+
+	/* Check if the root fs is on EVMS */
+	if (MAJOR(ROOT_DEV) == EVMS_MAJOR) {
+		find_root_fs_dev();
+	}
+	return 0;
+}
+
+__initcall(evms_init_discover);
+
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/