"Fossies" - the Fresh Open Source Software archive

Member "evms-2.5.5/plugins/multipath/multipath.c" of archive evms-2.5.5.tar.gz:


/*
 *   (C) Copyright IBM Corp. 2004
 *
 *   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
 *
 * Module: Multipath Plugin
 * File: evms2/engine/plugins/multipath/multipath.c
 *
 * This is the generic EVMS multipath plugin. It is intended to detect and
 * activate multipath devices based on multiple types of metadata (and even
 * without metadata). It utilizes the multipath module in Device-Mapper.
 */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <plugin.h>
#include "multipath.h"

/* Pointer to engine-services table. */
engine_functions_t *EngFncs;

/**
 * mp_modules
 *
 * Array of multipath sub-modules. Index into this array using a
 * multipath_type_t value.
 **/
multipath_module_t mp_modules[] = {
	{ .name		= "lvm",
	  .setup	= mp_lvm_setup,
	  .cleanup	= mp_lvm_cleanup,
	  .probe	= mp_lvm_probe,
	  .process	= mp_lvm_process,
	  .allocate	= mp_lvm_allocate,
	  .discard	= mp_lvm_discard,
	  .delete	= mp_lvm_delete,
	  .map		= mp_lvm_map,
	  .build_targets= mp_lvm_build_targets,
	},
};

/**
 * multipath_setup_evms_plugin
 *
 * Perform all setup when the plugin is loaded.
 **/
static int multipath_setup_evms_plugin(engine_functions_t *functions)
{
	int rc, i;

	EngFncs	= functions;

	LOG_ENTRY();

	/* Setup each sub-module. */
	for (i = 0; i < MULTIPATH_TYPE_MAX; i++) {
		rc = mp_modules[i].setup();
		if (rc) {
			goto out;
		}
	}

	/* Register the base namespace. */
	rc = EngFncs->register_name(MP_NAME);
	if (rc) {
		goto out;
	}

out:
	if (rc) {
		for (; i >= 0; i--) {
			mp_modules[i].cleanup();
		}
		EngFncs->unregister_name(MP_NAME);
	}

	LOG_EXIT_INT(rc);
	return rc;
}

/**
 * multipath_cleanup_evms_plugin
 *
 * Cleanup the plugin when the engine closes.
 **/
static void multipath_cleanup_evms_plugin(void)
{
	int i;

	LOG_ENTRY();

	/* Cleanup each sub-module and release the namespace. */
	for (i = 0; i < MULTIPATH_TYPE_MAX; i++) {
		mp_modules[i].cleanup();
	}
	EngFncs->unregister_name(MP_NAME);

	LOG_EXIT_VOID();
}

/**
 * multipath_can_delete
 **/
static int multipath_can_delete(storage_object_t *object)
{
	LOG_ENTRY();

	LOG_EXIT_INT(0);
	return 0;
}

/**
 * multipath_can_unassign
 *
 * Just like can_delete().
 **/
static int multipath_can_unassign(storage_object_t *object)
{
	LOG_ENTRY();

	LOG_EXIT_INT(ENOSYS);
	return ENOSYS;
}

/**
 * multipath_can_set_volume
 *
 * We aren't yet going to support volumes directly on MP objects, since
 * we're initially targetting this for LVM MP-PVs.
 **/
static int multipath_can_set_volume(storage_object_t *object, boolean flag)
{
	LOG_ENTRY();

	LOG_EXIT_INT(EINVAL);
	return EINVAL;
}

/**
 * multipath_discover
 *
 * Probe all input objects for multipath metadata and create new segments as
 * necessary.
 **/
static int multipath_discover(list_anchor_t input_objects,
			      list_anchor_t output_objects,
			      boolean final_call)
{
	storage_object_t *child;
	list_element_t itr;
	int i, rc, count = 0;

	LOG_ENTRY();

	LIST_FOR_EACH(input_objects, itr, child) {

		/* Only interested in DATA objects. */
		if (child->data_type != DATA_TYPE) {
			LOG_DEBUG("%s is not a DATA object.\n", child->name);
			EngFncs->insert_thing(output_objects, child, 0, NULL);
			continue;
		}

		/* Don't re-examine multipath objects. */
		if (child->plugin == &multipath_plugin) {
			LOG_DEBUG("%s is a multipath object.\n", child->name);
			EngFncs->insert_thing(output_objects, child, 0, NULL);
			continue;
		}

		/* Give each sub-module a chance to see if they recognize
		 * this object. If they do, they'll put it on an internal
		 * list to be processed later in discovery.
		 */
		for (i = 0; i < MULTIPATH_TYPE_MAX; i++) {
			rc = mp_modules[i].probe(child);
			if (!rc) {
				break;
			}
		}

		if (i == MULTIPATH_TYPE_MAX) {
			/* If nobody recognizes it, put it on the output list. */
			EngFncs->insert_thing(output_objects, child, 0, NULL);
		}
	}

	/* Now process each type-specific list to actually construct the
	 * multipath segments.
	 */
	for (i = 0; i < MULTIPATH_TYPE_MAX; i++) {
		count += mp_modules[i].process(output_objects);
	}

	if (final_call) {
		cleanup_stale_daemons();
	}

	LOG_EXIT_INT(count);
	return count;
}

/**
 * multipath_discard
 *
 * Forget about these objects. Don't delete them. Just clean up any data
 * structures you may have associated with them. The Engine will call to
 * deactivate the objects during commit.
 *
 * Since we need part of the private data to properly deactivate, we should
 * only free it if the object is not active (and hence doesn't need to be
 * deactivated). If the object is active, we'll free the private-data after
 * deactivation.
 **/
static int multipath_discard(list_anchor_t objects)
{
	storage_object_t *object;
	list_element_t itr;
	multipath_t *mp;

	LOG_ENTRY();

	LIST_FOR_EACH(objects, itr, object) {
		mp = object->private_data;
		mp_modules[mp->type].discard(object);
		if (!(object->flags & SOFLAG_ACTIVE)) {
			EngFncs->engine_free(mp);
			object->private_data = NULL;
		} else {
			mp->flags |= MP_FLAG_DELETE_PRIVATE_DATA;
		}
		EngFncs->free_segment(object);
	}

	LOG_EXIT_INT(0);
	return 0;
}

/**
 * multipath_delete
 *
 * Delete this segment. Erase all necessary metadata and free up the private
 * data structures.
 *
 * Since we need part of the private data to properly deactivate, we should
 * only free it if the object is not active (and hence doesn't need to be
 * deactivated). If the object is active, we'll free the private-data after
 * deactivation.
 **/
static int multipath_delete(storage_object_t *object,
			    list_anchor_t child_objects)
{
	multipath_t *mp = object->private_data;

	LOG_ENTRY();

	EngFncs->concatenate_lists(child_objects, object->child_objects);

	mp_modules[mp->type].delete(object);
	if (!(object->flags & SOFLAG_ACTIVE)) {
		EngFncs->engine_free(mp);
		object->private_data = NULL;
	} else {
		mp->flags |= MP_FLAG_DELETE_PRIVATE_DATA;
	}
	EngFncs->free_segment(object);

	LOG_EXIT_INT(0);
	return 0;
}

/**
 * multipath_unassign
 *
 * Don't support unassign yet.
 **/
static int multipath_unassign(storage_object_t *object)
{
	LOG_ENTRY();

	LOG_EXIT_INT(ENOSYS);
	return ENOSYS;
}

/**
 * multipath_add_sectors_to_kill_list
 *
 * Send the kill-sectors command to one of the child objects.
 **/
static int multipath_add_sectors_to_kill_list(storage_object_t *object,
					      lsn_t lsn, sector_count_t count)
{
	multipath_t *mp = object->private_data;
	int rc = EIO;

	LOG_ENTRY();

	rc = mp_modules[mp->type].map(&object, &lsn, &count);
	if (!rc) {
		rc = KILL_SECTORS(object, lsn, count);
	}

	LOG_EXIT_INT(rc);
	return rc;
}

/**
 * multipath_commit_changes
 *
 * Nothing to do yet for commit, since we don't yet have any metadata.
 **/
static int multipath_commit_changes(storage_object_t *object,
				    commit_phase_t phase)
{
	LOG_ENTRY();

	LOG_EXIT_INT(0);
	return 0;
}

/**
 * multipath_can_activate
 *
 * Multipath segments can always be activated.
 **/
static int multipath_can_activate(storage_object_t *object)
{
	LOG_ENTRY();
	LOG_EXIT_INT(0);
	return 0;
}

/**
 * multipath_activate
 *
 * Activate this multipath segment using Device-Mapper.
 **/
static int multipath_activate(storage_object_t *object)
{
	int rc;

	LOG_ENTRY();

	rc = stop_daemon(object);
	if (rc) {
		goto out;
	}

	rc = activate_segment(object);
	if (rc) {
		goto out;
	}

	rc = start_daemon(object);
	if (rc) {
		goto out;
	}

	object->flags &= ~SOFLAG_NEEDS_ACTIVATE;

out:
	LOG_EXIT_INT(rc);
	return rc;
}

/**
 * multipath_can_deactivate
 *
 * Multipath segments can always be deactivated.
 **/
static int multipath_can_deactivate(storage_object_t *object)
{
	LOG_ENTRY();
	LOG_EXIT_INT(0);
	return 0;
}

/**
 * multipath_deactivate
 *
 * Deactivation will only happen when the segment has been deleted or
 * discarded. Since we need part of the private-data to do the deactivation,
 * we need to free that private-data once we're done with it.
 **/
static int multipath_deactivate(storage_object_t *object)
{
	multipath_t *mp = object->private_data;
	int rc;

	LOG_ENTRY();

	stop_daemon(object);

	rc = EngFncs->dm_deactivate(object);
	if (!rc) {
		object->flags &= ~SOFLAG_NEEDS_DEACTIVATE;
		if (mp->flags & MP_FLAG_DELETE_PRIVATE_DATA) {
			/* If this deactivate is due to deleting or discarding
			 * the segment, we need to delete the private data.
			 */
			EngFncs->engine_free(mp);
		}
	}

	LOG_EXIT_INT(rc);
	return rc;
}

/**
 * multipath_get_info
 *
 * Return any additional information that you wish to provide about the object.
 * The Engine provides an external API to get the information stored in the
 * storage_object_t. This call is to get any other information about the object
 * that is not specified in the storage_object_t. Any piece of information you
 * wish to provide must be in an extended_info_t structure. Use the Engine's
 * engine_alloc() to allocate the memory for the extended_info_t. Also use
 * engine_alloc() to allocate any strings that may go into the extended_info_t.
 * Then use engine_alloc() to allocate an extended_info_array_t with enough
 * entries for the number of extended_info_t structures you are returning. Fill
 * in the array and return it in *info.
 *
 * If you have extended_info_t descriptors that themselves may have more
 * extended information, set the EVMS_EINFO_FLAGS_MORE_INFO_AVAILABLE flag in
 * the extended_info_t flags field. If the caller wants more information about
 * a particular extended_info_t item, this API will be called with a pointer to
 * the storage_object_t and with a pointer to the name of the extended_info_t
 * item. In that case, return an extended_info_array_t with further information
 * about the item. Each of those items may have the
 * EVMS_EINFO_FLAGS_MORE_INFO_AVAILABLE flag set if you desire. It is your
 * responsibility to give the items unique names so that you know which item the
 * caller is asking additional information for. If info_name is NULL, the caller
 * just wants top level information about the object.
 *
 * Return:
 * - 0 for success.
 * - Non-zero error-code for failure.
 *
 * OPTIONAL
 **/
static int multipath_get_info(storage_object_t *object,
			      char *info_name,
			      extended_info_array_t **info)
{
	LOG_ENTRY();

	LOG_EXIT_INT(ENOSYS);
	return ENOSYS;
}

/**
 * multipath_get_plugin_info
 *
 * Return any additional information that you wish to provide about your plugin.
 * The Engine provides an external API to get the information stored in the
 * plugin_record_t. This call is to get any other information about the plugin
 * that is not specified in the plugin_record_t. Any piece of information you
 * wish to provide must be in an extended_info_t structure. Use the Engine's
 * engine_alloc() to allocate the memory for the extended_info_t.  Also use
 * engine_alloc() to allocate any strings that may go into the extended_info_t.
 * Then use engine_alloc() to allocate an extended_info_array_t with enough
 * entries for the number of extended_info_t structures you are returning. Fill
 * in the array and return it in *info.
 *
 * If you have extended_info_t descriptors that themselves may have more
 * extended information, set the EVMS_EINFO_FLAGS_MORE_INFO_AVAILABLE flag in
 * the extended_info_t flags field. If the caller wants more information about
 * a particular extended_info_t item, this API will be called with a pointer to
 * the name of the extended_info_t item. In that case, return an
 * extended_info_array_t with further information about the item. Each of those
 * items may have the EVMS_EINFO_FLAGS_MORE_INFO_AVAILABLE flag set if you
 * desire. It is your responsibility to give the items unique names so that you
 * know which item the caller is asking additional information for. If
 * info_name is NULL, the caller just wants top level information about the
 * plugin.
 *
 * Return:
 * - 0 for success.
 * - Non-zero error-code for failure.
 *
 * OPTIONAL
 **/
static int multipath_get_plugin_info(char *info_name,
				     extended_info_array_t **info)
{
	LOG_ENTRY();

	LOG_EXIT_INT(ENOSYS);
	return ENOSYS;
}

/**
 * multipath_read
 *
 * Read from one of the child objects.
 **/
static int multipath_read(storage_object_t *object, lsn_t lsn,
			  sector_count_t count, void *buffer)
{
	multipath_t *mp = object->private_data;
	int rc;

	LOG_ENTRY();

	rc = mp_modules[mp->type].map(&object, &lsn, &count);
	if (!rc) {
		rc = READ(object, lsn, count, buffer);
	}

	LOG_EXIT_INT(rc);
	return rc;
}

/**
 * multipath_write
 *
 * Write to one of the child objects.
 **/
static int multipath_write(storage_object_t *object, lsn_t lsn,
			   sector_count_t count, void *buffer)
{
	multipath_t *mp = object->private_data;
	int rc;

	LOG_ENTRY();

	rc = mp_modules[mp->type].map(&object, &lsn, &count);
	if (!rc) {
		rc = WRITE(object, lsn, count, buffer);
	}

	LOG_EXIT_INT(rc);
	return rc;
}

/**
 * multipath_backup_metadata
 *
 * Nothing to do yet for backup, since we don't yet have any metadata.
 **/
static int multipath_backup_metadata(storage_object_t *object)
{
	LOG_ENTRY();

	LOG_EXIT_INT(0);
	return 0;
}

/* Plugin-Functions table for the Multipath plugin. */
static plugin_functions_t multipath_functions = {
	.setup_evms_plugin		= multipath_setup_evms_plugin,
	.cleanup_evms_plugin		= multipath_cleanup_evms_plugin,
	.can_delete			= multipath_can_delete,
	.can_unassign			= multipath_can_unassign,
	.can_set_volume			= multipath_can_set_volume,
	.discover			= multipath_discover,
	.discard			= multipath_discard,
	.delete				= multipath_delete,
	.unassign			= multipath_unassign,
	.add_sectors_to_kill_list	= multipath_add_sectors_to_kill_list,
	.commit_changes			= multipath_commit_changes,
	.can_activate			= multipath_can_activate,
	.activate			= multipath_activate,
	.can_deactivate			= multipath_can_deactivate,
	.deactivate			= multipath_deactivate,
	.get_info			= multipath_get_info,
	.get_plugin_info		= multipath_get_plugin_info,
	.read				= multipath_read,
	.write				= multipath_write,
	.backup_metadata		= multipath_backup_metadata,
};

/* Plugin-record for the Multipath plugin.*/
plugin_record_t multipath_plugin = {
	.id = EVMS_MULTIPATH_PLUGIN_ID,
	.version = {
		.major		= MAJOR_VERSION,
		.minor		= MINOR_VERSION,
		.patchlevel	= PATCH_LEVEL
	},
	.required_engine_api_version = {
		.major		= 15,
		.minor		= 0,
		.patchlevel	= 0
	},
	.required_plugin_api_version = {
		.plugin	= {
			.major		= 13,
			.minor		= 1,
			.patchlevel	= 0
		}
	},
	.short_name = EVMS_MULTIPATH_PLUGIN_SHORT_NAME,
	.long_name = EVMS_MULTIPATH_PLUGIN_LONG_NAME,
	.oem_name = EVMS_IBM_OEM_NAME,
	.functions = {
		.plugin = &multipath_functions
	},
};

plugin_record_t *evms_plugin_records[] = {
	&multipath_plugin,
	NULL
};