[PATCH] EVMS core (3/9) discover.c

Kevin Corry (corryk@us.ibm.com)
Thu, 10 Oct 2002 14:35:29 -0500


Greetings,

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/