/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#pragma ident	"%Z%%M%	%I%	%E% SMI"

#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stddef.h>
#include <stdlib.h>
#include <dirent.h>
#include <dlfcn.h>
#include <link.h>
#include <strings.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/mnttab.h>
#include <config_admin.h>
#include <sys/param.h>
#include <libintl.h>
#include <libdevinfo.h>
#include <raidcfg.h>
#include <thread.h>
#include <synch.h>

#ifndef TEXT_DOMAIN
#define	TEXT_DOMAIN	"SYS_TEST"
#endif

#define	HASH_SLOTS	16
#define	HANDLER_SLOTS	256

/*
 * Raid object status;
 */
#define	OBJ_STATUS_CMD_CLEAN	-1
#define	OBJ_STATUS_OPENED	1
#define	OBJ_STATUS_SCANCOMP	1 << 1

#if defined(__sparcv9)
#define	SUPP_PLUGIN_DIR	"/usr/lib/raidcfg/sparcv9"
#elif defined(__amd64)
#define	SUPP_PLUGIN_DIR	"/usr/lib/raidcfg/amd64"
#else
#define	SUPP_PLUGIN_DIR	"/usr/lib/raidcfg"
#endif

/*
 * Basic types
 */
typedef	int raid_obj_id_t;
typedef	int raid_obj_status_t;

/*
 * Data structures used for object maintennance
 */
typedef	struct {
	void *head;
	void *tail;
	size_t offset;	/* offset of double-linked element (raid_list_el_t) */
			/* in the linked data structures (objects) */
} raid_list_t;

typedef	struct {
	void *prev;
	void *next;
} raid_list_el_t;

typedef	struct {
	raid_obj_id_t obj_id_cnt;	/* id 0 is reserved */
	size_t slots;			/* How many lists linked by *table */
	raid_list_t *table;
} raid_obj_tab_t;

/*
 * Object type structure containing function pointers;
 */
typedef	struct {
	int (*compnum)(raid_obj_tab_t *, raid_obj_id_t, raid_obj_type_id_t);
	int (*complist)(raid_obj_tab_t *, raid_obj_id_t, int, raid_obj_id_t *,
		raid_obj_type_id_t);
	int (*get_attr)(raid_obj_tab_t *, raid_obj_id_t);
	int (*set_attr)(raid_obj_tab_t *, raid_obj_id_t, uint32_t, uint32_t *,
		char **);
	int (*act)(raid_obj_tab_t *, raid_obj_id_t, uint32_t, void *, char **);
	int (*create_obj)(raid_obj_tab_t *, raid_obj_id_t, int,
		raid_obj_id_t *, char **);
	int (*delete_obj)(raid_obj_tab_t *, raid_obj_id_t, char **);
	int (*bind_obj)(raid_obj_tab_t *, int, raid_obj_id_t *, char **);
	int (*unbind_obj)(raid_obj_tab_t *, int, raid_obj_id_t *, char **);
} raid_obj_op_t;

/*
 * Common object data structure
 */
typedef	struct {
	raid_list_el_t		el;	/* double-links */

	raid_obj_type_id_t	obj_type_id;
	raid_obj_id_t		obj_id;
	raid_obj_status_t	status;

	raid_obj_id_t		container;
	raid_obj_id_t		sibling;
	raid_obj_id_t		component;

	void			*data;	/* Pointer to attribute structure */
	raid_obj_handle_t	handle;
} raid_obj_t;

/*
 * Definition about handle
 */
typedef	struct {
	uint32_t	next;
	uint32_t	type;
	uint32_t	controller_id;
	uint32_t	array_id;
	uint32_t	disk_id;
	uint64_t	seq_id;
	uint32_t	task_id;
	uint32_t	fd;		/* Only for controller */
	raid_lib_t	*raid_lib;	/* Only for controller */
} handle_attr_t;

#define	LIST_OBJ_TO_EL(list, obj)	\
	((void *)((char *)(obj) + (list)->offset))
#define	OBJ_TAB_SLOT(tab, id)	\
	((tab)->table + ((id)%(tab)->slots))

#pragma init(raidcfg_init)
#pragma fini(raidcfg_fini)

/*
 * Function prototypes
 */
static int intcompare(const void *p1, const void *p2);
static uint64_t raid_space_noalign(raid_obj_tab_t *, uint32_t, int,
	raid_obj_id_t *, arraypart_attr_t *);
static int raid_dev_config(cfga_cmd_t, uint32_t, uint32_t, uint8_t);
static int raid_handle_init();
static void raid_handle_fini();
static raid_obj_handle_t raid_handle_new(raid_obj_type_id_t);
static void raid_handle_delete(raid_obj_handle_t);
static void raid_handle_delete_controller_comp(uint32_t);
static raid_obj_id_t raid_handle_to_obj(raid_obj_tab_t *,
	raid_obj_handle_t);
static raid_obj_handle_t raid_obj_to_handle(raid_obj_tab_t *,
	raid_obj_id_t);
static raid_lib_t *raid_obj_get_lib(raid_obj_tab_t *, raid_obj_id_t);
static int raid_obj_set_lib(raid_obj_tab_t *, raid_obj_id_t, raid_lib_t *);
static int raid_obj_get_fd(raid_obj_tab_t *, raid_obj_id_t);
static int raid_obj_set_fd(raid_obj_tab_t *, raid_obj_id_t, int);
static int obj_scan_comp(raid_obj_tab_t *, raid_obj_id_t);
static int obj_rescan(raid_obj_tab_t *);
static raid_obj_id_t obj_get_comp(raid_obj_tab_t *, raid_obj_id_t,
	raid_obj_type_id_t);
static raid_obj_id_t obj_get_sibling(raid_obj_tab_t *, raid_obj_id_t);
static int obj_get_attr(raid_obj_tab_t *, raid_obj_id_t, void **);
static raid_obj_id_t obj_locate_controller(raid_obj_tab_t *, uint32_t);
static raid_obj_id_t obj_locate_array(raid_obj_tab_t *, uint32_t, uint32_t);
static raid_obj_id_t obj_locate_array_recur(raid_obj_tab_t *, raid_obj_id_t,
	uint32_t);
static raid_obj_id_t obj_locate_hsp(raid_obj_tab_t *, uint32_t,
	uint32_t, uint32_t);
static raid_obj_id_t obj_locate_disk(raid_obj_tab_t *, uint32_t, uint32_t);
static raid_obj_id_t obj_locate_arraypart(raid_obj_tab_t *, uint32_t,
	uint32_t, uint32_t);
static raid_obj_id_t obj_locate_diskseg(raid_obj_tab_t *, uint32_t,
	uint32_t, uint32_t);
static raid_obj_id_t obj_locate_task(raid_obj_tab_t *, uint32_t, uint32_t);
static raid_obj_id_t obj_get_controller(raid_obj_tab_t *, raid_obj_id_t);

static int obj_sys_compnum(raid_obj_tab_t *, raid_obj_id_t,
	raid_obj_type_id_t);
static int obj_sys_complist(raid_obj_tab_t *, raid_obj_id_t, int,
	raid_obj_id_t *, raid_obj_type_id_t);
static int obj_controller_compnum(raid_obj_tab_t *, raid_obj_id_t,
	raid_obj_type_id_t);
static int obj_controller_complist(raid_obj_tab_t *, raid_obj_id_t, int,
	raid_obj_id_t *, raid_obj_type_id_t);
static int obj_controller_get_attr(raid_obj_tab_t *, raid_obj_id_t);
static int obj_controller_act(raid_obj_tab_t *, raid_obj_id_t,
	uint32_t, void *, char **);
static int obj_array_compnum(raid_obj_tab_t *, raid_obj_id_t,
	raid_obj_type_id_t);
static int obj_array_complist(raid_obj_tab_t *, raid_obj_id_t, int,
	raid_obj_id_t *, raid_obj_type_id_t);
static int obj_array_get_attr(raid_obj_tab_t *, raid_obj_id_t);
static int obj_array_set_attr(raid_obj_tab_t *, raid_obj_id_t,
	uint32_t, uint32_t *, char **);
static int obj_disk_compnum(raid_obj_tab_t *, raid_obj_id_t,
	raid_obj_type_id_t);
static int obj_disk_complist(raid_obj_tab_t *, raid_obj_id_t, int,
	raid_obj_id_t *, raid_obj_type_id_t);
static int obj_disk_get_attr(raid_obj_tab_t *, raid_obj_id_t);
static int obj_hsp_get_attr(raid_obj_tab_t *, raid_obj_id_t);
static int obj_arraypart_get_attr(raid_obj_tab_t *, raid_obj_id_t);
static int obj_diskseg_get_attr(raid_obj_tab_t *, raid_obj_id_t);
static int obj_task_get_attr(raid_obj_tab_t *, raid_obj_id_t);
static int obj_array_create(raid_obj_tab_t *, raid_obj_id_t, int,
	raid_obj_id_t *, char **);
static int obj_array_delete(raid_obj_tab_t *, raid_obj_id_t, char **);
static int obj_hsp_bind(raid_obj_tab_t *, int, raid_obj_id_t *, char **);
static int obj_hsp_unbind(raid_obj_tab_t *, int, raid_obj_id_t *, char **);

static int raid_obj_create_system_obj(raid_obj_tab_t *);
static raid_obj_id_t raid_obj_id_new(raid_obj_tab_t *);
static void *raid_obj_attr_new(raid_obj_type_id_t);
static raid_obj_id_t raid_obj_create(raid_obj_tab_t *, raid_obj_type_id_t);
static int raid_obj_delete(raid_obj_tab_t *, raid_obj_id_t);
static int raid_obj_add_org(raid_obj_tab_t *, raid_obj_id_t, raid_obj_id_t);
static raid_obj_type_id_t raid_obj_get_type(raid_obj_tab_t *, raid_obj_id_t);
static int raid_obj_set_type(raid_obj_tab_t *, raid_obj_id_t,
	raid_obj_type_id_t);
static raid_obj_status_t raid_obj_get_status(raid_obj_tab_t *, raid_obj_id_t);
static int raid_obj_set_status(raid_obj_tab_t *, raid_obj_id_t,
	raid_obj_status_t);
static int raid_obj_clear_status(raid_obj_tab_t *, raid_obj_id_t,
	raid_obj_status_t);
static raid_obj_id_t raid_obj_get_container(raid_obj_tab_t *, raid_obj_id_t);
static int raid_obj_set_container(raid_obj_tab_t *, raid_obj_id_t,
	raid_obj_id_t);
static raid_obj_id_t raid_obj_get_comp(raid_obj_tab_t *, raid_obj_id_t);
static int raid_obj_set_comp(raid_obj_tab_t *, raid_obj_id_t, raid_obj_id_t);
static raid_obj_id_t raid_obj_get_sibling(raid_obj_tab_t *, raid_obj_id_t);
static int raid_obj_set_sibling(raid_obj_tab_t *, raid_obj_id_t,
	raid_obj_id_t);
static void *raid_obj_get_data_ptr(raid_obj_tab_t *, raid_obj_id_t);
static int raid_obj_set_data_ptr(raid_obj_tab_t *, raid_obj_id_t, void *);
static raid_obj_handle_t raid_obj_get_handle(raid_obj_tab_t *,
	raid_obj_id_t);
static int raid_obj_set_handle(raid_obj_tab_t *, raid_obj_id_t,
	raid_obj_handle_t);

static void raid_list_create(raid_list_t *, size_t);
static void *raid_list_head(raid_list_t *);
static void *raid_list_next(raid_list_t *, void *);
static void raid_list_insert_tail(raid_list_t *, void *);
static void raid_list_remove(raid_list_t *, void *);
static void *raid_list_remove_head(raid_list_t *);
static void *raid_list_find(raid_list_t *, raid_obj_id_t);
static int raid_obj_tab_create(raid_obj_tab_t *, size_t);
static void raid_obj_tab_destroy(raid_obj_tab_t *);
static int raid_obj_tab_insert(raid_obj_tab_t *, raid_obj_id_t, void *);
static void *raid_obj_tab_remove(raid_obj_tab_t *, raid_obj_id_t);
static void *raid_obj_tab_find(raid_obj_tab_t *, raid_obj_id_t);
static void raid_list_destroy(raid_list_t *);

static int controller_id_to_path(uint32_t, char *);
static char *controller_id_to_driver_name(uint32_t);
static void raid_plugin_init();
static raid_lib_t *raid_plugin_load(char *);
static raid_lib_t *raid_find_lib(raid_obj_tab_t *, raid_obj_id_t);

/* Global object table */
static raid_obj_tab_t raid_tab_sys = {0, 0, NULL};

/* Plug-in modules maintenance data structures */
static raid_lib_t *raid_lib_sys = NULL;

/* Handle table definition */
static struct {
	int		handle_num;
	int		used;
	int		unused;
	handle_attr_t	*handles;
} raid_handle_sys = {0, 0, 0, NULL};

/*
 * RAID object method table definition
 */
static raid_obj_op_t raid_obj_op_sys[OBJ_TYPE_ALL] = {
	{obj_sys_compnum, obj_sys_complist, NULL, NULL, NULL,
		NULL, NULL, NULL, NULL},	/* system object methods */
	{obj_controller_compnum, obj_controller_complist,
		obj_controller_get_attr, NULL, obj_controller_act,
		NULL, NULL, NULL, NULL},	/* controller object methods */
	{obj_array_compnum, obj_array_complist, obj_array_get_attr,
		obj_array_set_attr, NULL, obj_array_create,
		obj_array_delete, NULL, NULL},	/* array object methods */
	{obj_disk_compnum, obj_disk_complist, obj_disk_get_attr, NULL,
		NULL, NULL, NULL, NULL, NULL},	/* disk object methods */
	{NULL, NULL, obj_hsp_get_attr, NULL, NULL, NULL, NULL, obj_hsp_bind,
		obj_hsp_unbind},		/* hsp object methods */
	{NULL, NULL, obj_arraypart_get_attr, NULL, NULL, NULL, NULL,
		NULL, NULL},			/* array part object methods */
	{NULL, NULL, obj_diskseg_get_attr, NULL, NULL, NULL, NULL, NULL, NULL},
	{NULL, NULL, obj_task_get_attr, NULL, NULL, NULL, NULL,
		NULL, NULL}			/* disk seg object methods */
};

/*
 * Mutex for multithread safe
 */
static mutex_t raidcfg_mp;

/*
 * RaidCfg library APIs
 */
const char *
raidcfg_errstr(int err_code)
{
	char *ret_val;

	(void) mutex_lock(&raidcfg_mp);
	switch (err_code) {
	case	SUCCESS:
		ret_val = dgettext(TEXT_DOMAIN, "Operation succeeded.\n");
		break;
	case	STD_IOCTL:
		ret_val = dgettext(TEXT_DOMAIN,
		    "Request standard IOCTL service.\n");
		break;
	case	ERR_DRIVER_NOT_FOUND:
		ret_val = dgettext(TEXT_DOMAIN,
		    "Controller device can not be found.\n");
		break;
	case	ERR_DRIVER_OPEN:
		ret_val = dgettext(TEXT_DOMAIN, "Can not open controller.\n");
		break;
	case	ERR_DRIVER_LOCK:
		ret_val = dgettext(TEXT_DOMAIN, "Controller is locked.\n");
		break;
	case	ERR_DRIVER_CLOSED:
		ret_val = dgettext(TEXT_DOMAIN, "Controller is not opened.\n");
		break;
	case	ERR_DRIVER_ACROSS:
		ret_val = dgettext(TEXT_DOMAIN,
		    "Operation across multiple controllers.\n");
		break;
	case	ERR_ARRAY_LEVEL:
		ret_val = dgettext(TEXT_DOMAIN,
		    "Operation not support with volume of this level.\n");
		break;
	case	ERR_ARRAY_SIZE:
		ret_val = dgettext(TEXT_DOMAIN,
		    "Capacity of array out of range.\n");
		break;
	case	ERR_ARRAY_STRIPE_SIZE:
		ret_val = dgettext(TEXT_DOMAIN, "Illegal stripe size.\n");
		break;
	case	ERR_ARRAY_CACHE_POLICY:
		ret_val = dgettext(TEXT_DOMAIN,
		    "Illegal cache-write policy.\n");
		break;
	case	ERR_ARRAY_IN_USE:
		ret_val = dgettext(TEXT_DOMAIN, "Array or disk in use.\n");
		break;
	case	ERR_ARRAY_TASK:
		ret_val = dgettext(TEXT_DOMAIN, "Array has background task.\n");
		break;
	case	ERR_ARRAY_CONFIG:
		ret_val = dgettext(TEXT_DOMAIN,
		    "Configuration over device node failed.\n");
		break;
	case	ERR_ARRAY_DISKNUM:
		ret_val = dgettext(TEXT_DOMAIN, "Incorrect number of disks.\n");
		break;
	case	ERR_ARRAY_LAYOUT:
		ret_val = dgettext(TEXT_DOMAIN, "Illegal array layout.\n");
		break;
	case	ERR_ARRAY_AMOUNT:
		ret_val = dgettext(TEXT_DOMAIN, "Too many arrays.\n");
		break;
	case	ERR_DISK_STATE:
		ret_val = dgettext(TEXT_DOMAIN,
		    "Incorrect disk status for current operation.\n");
		break;
	case	ERR_DISK_SPACE:
		ret_val = dgettext(TEXT_DOMAIN, "No enough disk space.\n");
		break;
	case	ERR_DISK_SEG_AMOUNT:
		ret_val = dgettext(TEXT_DOMAIN, "Too many disk segments.\n");
		break;
	case	ERR_DISK_NOT_EMPTY:
		ret_val = dgettext(TEXT_DOMAIN, "Disk has occupied space.\n");
		break;
	case	ERR_DISK_TASK:
		ret_val = dgettext(TEXT_DOMAIN, "Disk has background task.\n");
		break;
	case	ERR_TASK_STATE:
		ret_val = dgettext(TEXT_DOMAIN,
		    "Incorrect task state for current operation.\n");
		break;
	case	ERR_OP_ILLEGAL:
		ret_val = dgettext(TEXT_DOMAIN, "Illegal operation.\n");
		break;
	case	ERR_OP_NO_IMPL:
		ret_val = dgettext(TEXT_DOMAIN,
		    "Operation is not implemented.\n");
		break;
	case	ERR_OP_FAILED:
		ret_val = dgettext(TEXT_DOMAIN, "Operation failed.\n");
		break;
	case	ERR_DEVICE_NOENT:
		ret_val = dgettext(TEXT_DOMAIN, "Device not found.\n");
		break;
	case	ERR_DEVICE_TYPE:
		ret_val = dgettext(TEXT_DOMAIN, "Illegal type of device.\n");
		break;
	case	ERR_DEVICE_DUP:
		ret_val = dgettext(TEXT_DOMAIN, "Device record duplicated.\n");
		break;
	case	ERR_DEVICE_OVERFLOW:
		ret_val = dgettext(TEXT_DOMAIN, "Too many devices.\n");
		break;
	case	ERR_DEVICE_UNCLEAN:
		ret_val = dgettext(TEXT_DOMAIN, "Device pool is not clean.\n");
		break;
	case	ERR_DEVICE_INVALID:
		ret_val = dgettext(TEXT_DOMAIN, "Device record is invalid.\n");
		break;
	case	ERR_NOMEM:
		ret_val = dgettext(TEXT_DOMAIN,
		    "Can not allocate more memory space.\n");
		break;
	case	ERR_PRIV:
		ret_val = dgettext(TEXT_DOMAIN, "No privilege.\n");
		break;
	default:
		ret_val = dgettext(TEXT_DOMAIN, "Undefined error.\n");
	}
	(void) mutex_unlock(&raidcfg_mp);

	return (ret_val);
}

int
raidcfg_get_controller(uint32_t controller_id)
{
	raid_obj_id_t obj_id;
	int ret_val;

	(void) mutex_lock(&raidcfg_mp);
	(void) obj_rescan(&raid_tab_sys);
	obj_id = obj_locate_controller(&raid_tab_sys, controller_id);
	if (obj_id < OBJ_NONE) {
		(void) mutex_unlock(&raidcfg_mp);
		return (obj_id);
	}

	if (obj_id == OBJ_NONE) {
		(void) mutex_unlock(&raidcfg_mp);
		return (ERR_DEVICE_NOENT);
	}
	ret_val = raid_obj_to_handle(&raid_tab_sys, obj_id);
	(void) mutex_unlock(&raidcfg_mp);

	return (ret_val);
}

int
raidcfg_get_array(int controller_handle, uint64_t target_id, uint64_t lun)
{
	raid_obj_id_t obj_id;
	raidcfg_array_t *attr;
	int ret_val;

	(void) mutex_lock(&raidcfg_mp);
	(void) obj_rescan(&raid_tab_sys);
	obj_id = raid_handle_to_obj(&raid_tab_sys, controller_handle);
	if (obj_id < OBJ_NONE) {
		(void) mutex_unlock(&raidcfg_mp);
		return (obj_id);
	}

	obj_id = obj_get_comp(&raid_tab_sys, obj_id, OBJ_TYPE_ARRAY);

	while (obj_id > OBJ_NONE) {
		(void) obj_get_attr(&raid_tab_sys, obj_id, (void **)(&attr));
		if (attr->tag.idl.target_id == target_id &&
		    attr->tag.idl.lun == lun)
			break;

		obj_id = obj_get_sibling(&raid_tab_sys, obj_id);
	}

	if (obj_id < OBJ_NONE) {
		(void) mutex_unlock(&raidcfg_mp);
		return (obj_id);
	}
	if (obj_id == OBJ_NONE) {
		(void) mutex_unlock(&raidcfg_mp);
		return (ERR_DEVICE_NOENT);
	}
	ret_val = raid_obj_to_handle(&raid_tab_sys, obj_id);
	(void) mutex_unlock(&raidcfg_mp);

	return (ret_val);
}

int
raidcfg_get_disk(int controller_handle, disk_tag_t tag)
{
	raid_obj_id_t obj_id;
	raidcfg_disk_t *attr;
	int ret_val;

	(void) mutex_lock(&raidcfg_mp);
	(void) obj_rescan(&raid_tab_sys);
	obj_id = raid_handle_to_obj(&raid_tab_sys, controller_handle);
	if (obj_id < OBJ_NONE) {
		(void) mutex_unlock(&raidcfg_mp);
		return (obj_id);
	}

	obj_id = obj_get_comp(&raid_tab_sys, obj_id, OBJ_TYPE_DISK);

	while (obj_id > OBJ_NONE) {
		(void) obj_get_attr(&raid_tab_sys, obj_id, (void **)(&attr));
		if (attr->tag.cidl.bus == tag.cidl.bus &&
		    attr->tag.cidl.target_id == tag.cidl.target_id &&
		    attr->tag.cidl.lun == tag.cidl.lun)
			break;

		obj_id = obj_get_sibling(&raid_tab_sys, obj_id);
	}

	if (obj_id < OBJ_NONE) {
		(void) mutex_unlock(&raidcfg_mp);
		return (obj_id);
	}
	if (obj_id == OBJ_NONE) {
		(void) mutex_unlock(&raidcfg_mp);
		return (ERR_DEVICE_NOENT);
	}
	ret_val = raid_obj_to_handle(&raid_tab_sys, obj_id);
	(void) mutex_unlock(&raidcfg_mp);

	return (ret_val);
}

int
raidcfg_open_controller(int handle, char **plugin_err_str)
{
	raid_obj_id_t obj_id;
	int ret;

	(void) mutex_lock(&raidcfg_mp);
	(void) obj_rescan(&raid_tab_sys);
	obj_id = raid_handle_to_obj(&raid_tab_sys, handle);
	if (obj_id < OBJ_NONE) {
		raid_handle_delete(handle);
		(void) mutex_unlock(&raidcfg_mp);
		return (ERR_DEVICE_NOENT);
	}

	ret = obj_controller_act(&raid_tab_sys, obj_id,
	    ACT_CONTROLLER_OPEN, NULL, plugin_err_str);
	if (ret < SUCCESS) {
		(void) mutex_unlock(&raidcfg_mp);
		return (ret);
	}
	(void) mutex_unlock(&raidcfg_mp);

	return (SUCCESS);
}

int
raidcfg_close_controller(int handle, char **plugin_err_str)
{
	raid_obj_id_t obj_id;
	int ret;

	(void) mutex_lock(&raidcfg_mp);
	(void) obj_rescan(&raid_tab_sys);
	obj_id = raid_handle_to_obj(&raid_tab_sys, handle);
	if (obj_id < OBJ_NONE) {
		raid_handle_delete(handle);
		(void) mutex_unlock(&raidcfg_mp);
		return (ERR_DEVICE_NOENT);
	}

	ret = obj_controller_act(&raid_tab_sys, obj_id,
	    ACT_CONTROLLER_CLOSE, NULL, plugin_err_str);
	if (ret < SUCCESS) {
		(void) mutex_unlock(&raidcfg_mp);
		return (ret);
	}
	(void) mutex_unlock(&raidcfg_mp);

	return (SUCCESS);
}

int
raidcfg_get_type(int handle)
{
	raid_obj_id_t obj_id;
	int ret_val;

	(void) mutex_lock(&raidcfg_mp);
	(void) obj_rescan(&raid_tab_sys);
	obj_id = raid_handle_to_obj(&raid_tab_sys, handle);
	if (obj_id < OBJ_NONE) {
		raid_handle_delete(handle);
		(void) mutex_unlock(&raidcfg_mp);
		return (ERR_DEVICE_NOENT);
	}
	ret_val = raid_obj_get_type(&raid_tab_sys, obj_id);
	(void) mutex_unlock(&raidcfg_mp);

	return (ret_val);
}

int
raidcfg_get_attr(int handle, void *attr)
{
	raid_obj_id_t obj_id;
	raid_obj_type_id_t type;
	void *data;
	int ret, size;

	(void) mutex_lock(&raidcfg_mp);
	(void) obj_rescan(&raid_tab_sys);
	if (attr == NULL) {
		(void) mutex_unlock(&raidcfg_mp);
		return (ERR_DEVICE_INVALID);
	}

	obj_id = raid_handle_to_obj(&raid_tab_sys, handle);
	if (obj_id < OBJ_NONE) {
		raid_handle_delete(handle);
		(void) mutex_unlock(&raidcfg_mp);
		return (ERR_DEVICE_NOENT);
	}

	type = raid_obj_get_type(&raid_tab_sys, obj_id);
	ret = obj_get_attr(&raid_tab_sys, obj_id, &data);
	if (ret < SUCCESS) {
		(void) mutex_unlock(&raidcfg_mp);
		return (ret);
	}

	switch (type) {
	case	OBJ_TYPE_CONTROLLER:
		size = sizeof (controller_attr_t);
		break;
	case	OBJ_TYPE_ARRAY:
		size = sizeof (array_attr_t);
		break;
	case	OBJ_TYPE_HSP:
		{
			raidcfg_hsp_t *dst = attr;
			hsp_attr_t *src = data;
			controller_attr_t *ctlr_attr;
			array_attr_t *array_attr;

			dst->associated_id = src->associated_id;
			dst->type = src->type;

			obj_id = obj_get_controller(&raid_tab_sys, obj_id);
			ret = obj_get_attr(&raid_tab_sys, obj_id,
			    (void **)(&ctlr_attr));
			if (ret < SUCCESS) {
				(void) mutex_unlock(&raidcfg_mp);
				return (ret);
			}

			if (src->type == HSP_TYPE_LOCAL) {
				obj_id = obj_locate_array(&raid_tab_sys,
				    ctlr_attr->controller_id,
				    src->associated_id);
				ret = obj_get_attr(&raid_tab_sys, obj_id,
				    (void **)(&array_attr));
				if (ret < SUCCESS) {
					(void) mutex_unlock(&raidcfg_mp);
					return (ret);
				}

				dst->tag.idl.target_id =
				    array_attr->tag.idl.target_id;
				dst->tag.idl.lun = array_attr->tag.idl.lun;
			}
		}
		(void) mutex_unlock(&raidcfg_mp);
		return (SUCCESS);
	case	OBJ_TYPE_DISK:
		size = sizeof (disk_attr_t);
		break;
	case	OBJ_TYPE_ARRAY_PART:
		{
			raidcfg_arraypart_t *dst = attr;
			arraypart_attr_t *src = data;
			controller_attr_t *ctlr_attr;
			disk_attr_t *disk_attr;

			dst->disk_id = src->disk_id;
			dst->offset = src->offset;
			dst->size = src->size;
			dst->state = src->state;

			obj_id = obj_get_controller(&raid_tab_sys, obj_id);
			ret = obj_get_attr(&raid_tab_sys, obj_id,
			    (void **)(&ctlr_attr));
			if (ret < SUCCESS) {
				(void) mutex_unlock(&raidcfg_mp);
				return (ret);
			}

			obj_id = obj_locate_disk(&raid_tab_sys,
			    ctlr_attr->controller_id, src->disk_id);
			if (obj_id <= OBJ_NONE) {
				dst->tag.cidl.bus = (uint64_t)OBJ_ATTR_NONE;
				dst->tag.cidl.target_id =
				    (uint64_t)OBJ_ATTR_NONE;
				dst->tag.cidl.lun = (uint64_t)OBJ_ATTR_NONE;
				(void) mutex_unlock(&raidcfg_mp);
				return (SUCCESS);
			}

			ret = obj_get_attr(&raid_tab_sys, obj_id,
			    (void **)(&disk_attr));
			if (ret < SUCCESS) {
				(void) mutex_unlock(&raidcfg_mp);
				return (ret);
			}

			dst->tag.cidl.bus = disk_attr->tag.cidl.bus;
			dst->tag.cidl.target_id = disk_attr->tag.cidl.target_id;
			dst->tag.cidl.lun = disk_attr->tag.cidl.lun;
		}
		(void) mutex_unlock(&raidcfg_mp);
		return (SUCCESS);
	case	OBJ_TYPE_DISK_SEG:
		size = sizeof (diskseg_attr_t);
		break;
	case	OBJ_TYPE_TASK:
		size = sizeof (task_attr_t);
		break;
	default:
		(void) mutex_unlock(&raidcfg_mp);
		return (ERR_DEVICE_TYPE);
	}

	(void) memcpy(attr, data, size);

	(void) mutex_unlock(&raidcfg_mp);
	return (ret);
}

int
raidcfg_get_container(int handle)
{
	raid_obj_id_t obj_id;
	int ret_val;

	(void) mutex_lock(&raidcfg_mp);
	(void) obj_rescan(&raid_tab_sys);
	obj_id = raid_handle_to_obj(&raid_tab_sys, handle);
	if (obj_id < OBJ_NONE) {
		raid_handle_delete(handle);
		(void) mutex_unlock(&raidcfg_mp);
		return (ERR_DEVICE_NOENT);
	}

	obj_id = raid_obj_get_container(&raid_tab_sys, obj_id);
	if (obj_id < OBJ_NONE) {
		(void) mutex_unlock(&raidcfg_mp);
		return (obj_id);
	}
	ret_val = raid_obj_to_handle(&raid_tab_sys, obj_id);
	(void) mutex_unlock(&raidcfg_mp);

	return (ret_val);
}

int
raidcfg_list_head(int handle, raid_obj_type_id_t type)
{
	raid_obj_id_t obj_id;
	int ret_val;

	(void) mutex_lock(&raidcfg_mp);
	(void) obj_rescan(&raid_tab_sys);
	obj_id = raid_handle_to_obj(&raid_tab_sys, handle);
	if (obj_id < OBJ_NONE) {
		raid_handle_delete(handle);
		(void) mutex_unlock(&raidcfg_mp);
		return (ERR_DEVICE_NOENT);
	}

	obj_id = obj_get_comp(&raid_tab_sys, obj_id, type);
	if (obj_id < OBJ_NONE) {
		(void) mutex_unlock(&raidcfg_mp);
		return (obj_id);
	}
	ret_val = raid_obj_to_handle(&raid_tab_sys, obj_id);
	(void) mutex_unlock(&raidcfg_mp);

	return (ret_val);
}

int
raidcfg_list_next(int handle)
{
	raid_obj_id_t obj_id;
	int ret_val;

	(void) mutex_lock(&raidcfg_mp);
	(void) obj_rescan(&raid_tab_sys);
	obj_id = raid_handle_to_obj(&raid_tab_sys, handle);
	if (obj_id < OBJ_NONE) {
		raid_handle_delete(handle);
		(void) mutex_unlock(&raidcfg_mp);
		return (ERR_DEVICE_NOENT);
	}

	obj_id = obj_get_sibling(&raid_tab_sys, obj_id);
	if (obj_id < OBJ_NONE) {
		(void) mutex_unlock(&raidcfg_mp);
		return (obj_id);
	}
	ret_val = raid_obj_to_handle(&raid_tab_sys, obj_id);
	(void) mutex_unlock(&raidcfg_mp);

	return (ret_val);
}

int
raidcfg_set_attr(int handle, uint32_t set_cmd, void *value,
	char **plugin_err_str)
{
	raid_obj_id_t obj_id;
	raid_obj_type_id_t type;
	int ret;

	(void) mutex_lock(&raidcfg_mp);
	(void) obj_rescan(&raid_tab_sys);
	obj_id = raid_handle_to_obj(&raid_tab_sys, handle);
	if (obj_id < OBJ_NONE) {
		raid_handle_delete(handle);
		(void) mutex_unlock(&raidcfg_mp);
		return (ERR_DEVICE_NOENT);
	}

	type = raid_obj_get_type(&raid_tab_sys, obj_id);
	if (raid_obj_op_sys[type].set_attr == NULL) {
		(void) mutex_unlock(&raidcfg_mp);
		return (ERR_OP_NO_IMPL);
	}

	ret = raid_obj_op_sys[type].set_attr(&raid_tab_sys,
	    obj_id, set_cmd, value, plugin_err_str);

	(void) mutex_unlock(&raidcfg_mp);
	return (ret);
}

int
raidcfg_update_fw(int handle, char *file, char **plugin_err_str)
{
	raid_obj_id_t obj_id;
	int ret;

	(void) mutex_lock(&raidcfg_mp);
	(void) obj_rescan(&raid_tab_sys);
	obj_id = raid_handle_to_obj(&raid_tab_sys, handle);
	if (obj_id < OBJ_NONE) {
		raid_handle_delete(handle);
		(void) mutex_unlock(&raidcfg_mp);
		return (ERR_DEVICE_NOENT);
	}

	if (raid_obj_get_type(&raid_tab_sys, obj_id) != OBJ_TYPE_CONTROLLER) {
		(void) mutex_unlock(&raidcfg_mp);
		return (ERR_OP_NO_IMPL);
	}

	ret = raid_obj_op_sys[OBJ_TYPE_CONTROLLER].act(&raid_tab_sys,
	    obj_id, ACT_CONTROLLER_FLASH_FW, file, plugin_err_str);

	(void) mutex_unlock(&raidcfg_mp);
	return (ret);
}

int
raidcfg_create_array(int num_of_comps, int *disk_handles,
	uint32_t raid_level, uint64_t size, uint32_t stripe_size,
	char **plugin_err_str)
{
	raid_obj_id_t *disk_obj_ids, obj_id;
	array_attr_t *array_attr;
	raid_obj_handle_t array_handle;
	int i, ret;

	(void) mutex_lock(&raidcfg_mp);
	(void) obj_rescan(&raid_tab_sys);

	disk_obj_ids = calloc(num_of_comps, sizeof (raid_obj_id_t));
	if (disk_obj_ids == NULL) {
		(void) mutex_unlock(&raidcfg_mp);
		return (ERR_NOMEM);
	}

	/* convert disk handles into disk object ids; */
	for (i = 0; i < num_of_comps; ++i) {
		if (*(disk_handles + i) == OBJ_SEPARATOR_BEGIN ||
		    *(disk_handles + i) == OBJ_SEPARATOR_END) {
			*(disk_obj_ids + i) = *(disk_handles + i);
			continue;
		}

		*(disk_obj_ids + i) = raid_handle_to_obj(&raid_tab_sys,
		    *(disk_handles + i));
		if (raid_obj_get_type(&raid_tab_sys, *(disk_obj_ids + i)) !=
		    OBJ_TYPE_DISK) {
			free(disk_obj_ids);
			(void) obj_rescan(&raid_tab_sys);
			(void) mutex_unlock(&raidcfg_mp);
			return (ERR_DEVICE_TYPE);
		}
	}

	/* Create an empty array object */
	obj_id = raid_obj_create(&raid_tab_sys, OBJ_TYPE_ARRAY);
	if (obj_id < OBJ_NONE) {
		free(disk_obj_ids);
		(void) obj_rescan(&raid_tab_sys);
		(void) mutex_unlock(&raidcfg_mp);
		return (obj_id);
	}
	(void) raid_obj_clear_status(&raid_tab_sys, obj_id,
	    OBJ_STATUS_CMD_CLEAN);

	array_attr = raid_obj_get_data_ptr(&raid_tab_sys, obj_id);
	array_attr->array_id = (uint32_t)OBJ_ATTR_NONE;
	array_attr->raid_level = raid_level;
	array_attr->capacity = size;
	array_attr->stripe_size = stripe_size;
	array_attr->write_policy = CACHE_WR_ON;
	array_attr->read_policy = CACHE_RD_ON;

	ret = raid_obj_op_sys[OBJ_TYPE_ARRAY].create_obj(&raid_tab_sys, obj_id,
	    num_of_comps, disk_obj_ids, plugin_err_str);
	free(disk_obj_ids);

	if (ret < SUCCESS) {
		(void) obj_rescan(&raid_tab_sys);
		(void) mutex_unlock(&raidcfg_mp);
		return (ret);
	}

	/* create_obj() method should put the array object in the device tree */
	array_handle = raid_obj_to_handle(&raid_tab_sys, obj_id);

	(void) obj_rescan(&raid_tab_sys);
	(void) mutex_unlock(&raidcfg_mp);
	return (array_handle);
}

int
raidcfg_delete_array(int array_handle, char **plugin_err_str)
{
	raid_obj_id_t array_obj_id;
	int ret;

	(void) mutex_lock(&raidcfg_mp);
	(void) obj_rescan(&raid_tab_sys);

	if (raidcfg_get_type(array_handle) != OBJ_TYPE_ARRAY) {
		(void) mutex_unlock(&raidcfg_mp);
		return (ERR_DEVICE_TYPE);
	}

	array_obj_id = raid_handle_to_obj(&raid_tab_sys, array_handle);
	if (array_obj_id < OBJ_NONE) {
		(void) mutex_unlock(&raidcfg_mp);
		return (array_obj_id);
	}
	if (array_obj_id == OBJ_NONE) {
		(void) mutex_unlock(&raidcfg_mp);
		return (ERR_DEVICE_INVALID);
	}

	ret = raid_obj_op_sys[OBJ_TYPE_ARRAY].delete_obj(&raid_tab_sys,
	    array_obj_id, plugin_err_str);
	(void) obj_rescan(&raid_tab_sys);

	(void) mutex_unlock(&raidcfg_mp);
	return (ret);
}

int
raidcfg_set_hsp(int num, raidcfg_hsp_relation_t *hsp_relations,
	char **plugin_err_str)
{
	raid_obj_id_t disk_obj_id, array_obj_id;
	raid_obj_id_t *hsp_relation_objs;
	int ret, i;

	(void) mutex_lock(&raidcfg_mp);
	(void) obj_rescan(&raid_tab_sys);
	if ((num == 0) || (hsp_relations == NULL)) {
		(void) mutex_unlock(&raidcfg_mp);
		return (ERR_OP_ILLEGAL);
	}

	hsp_relation_objs = malloc(2 * num * sizeof (raid_obj_id_t));
	if (hsp_relation_objs == NULL) {
		(void) mutex_unlock(&raidcfg_mp);
		return (ERR_NOMEM);
	}

	(void) obj_rescan(&raid_tab_sys);

	for (i = 0; i < num; ++ i) {
		if (hsp_relations->array_handle != OBJ_ATTR_NONE) {
			array_obj_id = raid_handle_to_obj(&raid_tab_sys,
			    hsp_relations[i].array_handle);
			if (array_obj_id < OBJ_NONE) {
				free(hsp_relation_objs);
				(void) mutex_unlock(&raidcfg_mp);
				return (array_obj_id);
			}
			if (array_obj_id == OBJ_NONE) {
				(void) free(hsp_relation_objs);
				(void) mutex_unlock(&raidcfg_mp);
				return (ERR_DEVICE_NOENT);
			}
			if (raidcfg_get_type(hsp_relations[i].array_handle) !=
			    OBJ_TYPE_ARRAY) {
				free(hsp_relation_objs);
				(void) mutex_unlock(&raidcfg_mp);
				return (ERR_DEVICE_TYPE);
			}
		} else
			array_obj_id = OBJ_ATTR_NONE;

		disk_obj_id = raid_handle_to_obj(&raid_tab_sys,
		    hsp_relations[i].disk_handle);
		if (disk_obj_id < OBJ_NONE) {
			free(hsp_relation_objs);
			(void) mutex_unlock(&raidcfg_mp);
			return (disk_obj_id);
		}
		if (disk_obj_id == OBJ_NONE) {
			free(hsp_relation_objs);
			(void) mutex_unlock(&raidcfg_mp);
			return (ERR_DEVICE_NOENT);
		}
		if (raidcfg_get_type(hsp_relations[i].disk_handle) !=
		    OBJ_TYPE_DISK) {
			free(hsp_relation_objs);
			(void) mutex_unlock(&raidcfg_mp);
			return (ERR_DEVICE_TYPE);
		}

		hsp_relation_objs[2 * i] = array_obj_id;
		hsp_relation_objs[2 * i + 1] = disk_obj_id;
	}

	ret = raid_obj_op_sys[OBJ_TYPE_HSP].bind_obj(&raid_tab_sys, num,
	    hsp_relation_objs, plugin_err_str);

	(void) obj_rescan(&raid_tab_sys);
	free(hsp_relation_objs);
	(void) mutex_unlock(&raidcfg_mp);

	return (ret);
}

int
raidcfg_unset_hsp(int num, raidcfg_hsp_relation_t *hsp_relations,
	char **plugin_err_str)
{
	raid_obj_id_t disk_obj_id, array_obj_id;
	raid_obj_id_t *hsp_relation_objs;
	int ret, i;

	(void) mutex_lock(&raidcfg_mp);
	(void) obj_rescan(&raid_tab_sys);
	if ((num == 0) || (hsp_relations == NULL)) {
		(void) mutex_unlock(&raidcfg_mp);
		return (ERR_OP_ILLEGAL);
	}

	hsp_relation_objs = malloc(2 * num * sizeof (raid_obj_id_t));
	if (hsp_relation_objs == NULL) {
		(void) mutex_unlock(&raidcfg_mp);
		return (ERR_NOMEM);
	}

	(void) obj_rescan(&raid_tab_sys);

	for (i = 0; i < num; ++ i) {
		if (hsp_relations->array_handle != OBJ_ATTR_NONE) {
			array_obj_id = raid_handle_to_obj(&raid_tab_sys,
			    hsp_relations[i].array_handle);
			if (array_obj_id < OBJ_NONE) {
				free(hsp_relation_objs);
				(void) mutex_unlock(&raidcfg_mp);
				return (array_obj_id);
			}
			if (array_obj_id == OBJ_NONE) {
				free(hsp_relation_objs);
				(void) mutex_unlock(&raidcfg_mp);
				return (ERR_DEVICE_NOENT);
			}
			if (raidcfg_get_type(hsp_relations[i].array_handle) !=
			    OBJ_TYPE_ARRAY) {
				free(hsp_relation_objs);
				(void) mutex_unlock(&raidcfg_mp);
				return (ERR_DEVICE_TYPE);
			}
		} else
			array_obj_id = OBJ_ATTR_NONE;

		disk_obj_id = raid_handle_to_obj(&raid_tab_sys,
		    hsp_relations[i].disk_handle);
		if (disk_obj_id < OBJ_NONE) {
			free(hsp_relation_objs);
			(void) mutex_unlock(&raidcfg_mp);
			return (disk_obj_id);
		}
		if (disk_obj_id == OBJ_NONE) {
			free(hsp_relation_objs);
			(void) mutex_unlock(&raidcfg_mp);
			return (ERR_DEVICE_NOENT);
		}
		if (raidcfg_get_type(hsp_relations[i].disk_handle) !=
		    OBJ_TYPE_DISK) {
			free(hsp_relation_objs);
			(void) mutex_unlock(&raidcfg_mp);
			return (ERR_DEVICE_TYPE);
		}

		hsp_relation_objs[2 * i] = array_obj_id;
		hsp_relation_objs[2 * i + 1] = disk_obj_id;
	}

	ret = raid_obj_op_sys[OBJ_TYPE_HSP].unbind_obj(&raid_tab_sys,
	    num, hsp_relation_objs, plugin_err_str);

	(void) obj_rescan(&raid_tab_sys);
	free(hsp_relation_objs);
	(void) mutex_unlock(&raidcfg_mp);

	return (ret);
}

/*
 * RaidCfg lib routines
 */
void
raidcfg_init(void)
{
	(void) mutex_init(&raidcfg_mp, NULL, NULL);
	raid_plugin_init();
	(void) raid_handle_init();
	(void) obj_rescan(&raid_tab_sys);
}

void
raidcfg_fini(void)
{
	/*
	 * Need to close all opened controllers before destroying object table
	 */
	(void) obj_rescan(&raid_tab_sys);
	raid_handle_fini();
	raid_obj_tab_destroy(&raid_tab_sys);
	raid_plugin_init();
	(void) mutex_destroy(&raidcfg_mp);
}

/*
 * Support routines
 */
static int
intcompare(const void *p1, const void *p2)
{
	int i, j;
	i = *((int *)p1);
	j = *((int *)p2);
	return (i - j);
}

static uint64_t
raid_space_noalign(raid_obj_tab_t *raid_tab, uint32_t raid_level, int num,
	raid_obj_id_t *disk_objs, arraypart_attr_t *arraypart_attrs)
{
	disk_attr_t *disk_attr;
	diskseg_attr_t *diskseg_attr;
	raid_obj_id_t obj_id;
	uint64_t offset, capacity;
	int i, disk_num, sub_array_num, disk_layer;

	/* Find out the maximum available space for all disks */
	for (i = 0; i < num; ++i) {
		if ((disk_objs[i] == OBJ_SEPARATOR_BEGIN) ||
		    (disk_objs[i] == OBJ_SEPARATOR_END))
			continue;

		(void) obj_get_attr(raid_tab, disk_objs[i],
		    (void **)(&disk_attr));
		obj_id = obj_get_comp(raid_tab, disk_objs[i],
		    OBJ_TYPE_DISK_SEG);
		if (obj_id == OBJ_NONE) {
			arraypart_attrs[i].offset = 0;
			arraypart_attrs[i].size = disk_attr->capacity;
			continue;
		}

		(void) obj_get_attr(raid_tab, obj_id, (void **)
		    (&diskseg_attr));
		arraypart_attrs[i].offset = 0;
		arraypart_attrs[i].size = diskseg_attr->offset;
		offset = diskseg_attr->offset + diskseg_attr->size;

		while ((obj_id = obj_get_sibling(raid_tab, obj_id)) !=
		    OBJ_NONE) {
			(void) obj_get_attr(raid_tab, obj_id,
			    (void **)(&diskseg_attr));
			if ((diskseg_attr->offset - offset) >
			    arraypart_attrs[i].size) {
				arraypart_attrs[i].offset = offset;
				arraypart_attrs[i].size = diskseg_attr->offset -
				    offset;
			}

			offset = diskseg_attr->offset + diskseg_attr->size;
		}

		if ((disk_attr->capacity - offset) > arraypart_attrs[i].size) {
			arraypart_attrs[i].offset = offset;
			arraypart_attrs[i].size = disk_attr->capacity -
			    offset;
		}
	}

	capacity = OBJ_ATTR_NONE;
	disk_num = 0;
	disk_layer = 0;
	sub_array_num = 0;
	for (i = 0; i < num; ++i) {
		if (disk_objs[i] == OBJ_SEPARATOR_BEGIN) {
			++ disk_layer;
			continue;
		}
		if (disk_objs[i] == OBJ_SEPARATOR_END) {
			-- disk_layer;
			if (disk_layer != 0)
				++ sub_array_num;
			continue;
		}

		if (capacity > arraypart_attrs[i].size)
			capacity = arraypart_attrs[i].size;
		++disk_num;
	}

	switch (raid_level) {
	case	RAID_LEVEL_0:
		capacity = capacity * disk_num;
		break;
	case	RAID_LEVEL_1:
		capacity = capacity * disk_num / 2;
		break;
	case	RAID_LEVEL_1E:
		capacity = capacity * disk_num / 2;
		break;
	case	RAID_LEVEL_5:
		capacity = capacity * (disk_num - 1);
		break;
	case	RAID_LEVEL_10:
		capacity = capacity * disk_num / 2;
		break;
	case	RAID_LEVEL_50:
		capacity = capacity * (disk_num - sub_array_num);
		break;
	default:
		return (ERR_ARRAY_LEVEL);
		break;
	}

	return (capacity);
}

static int
raid_dev_config(cfga_cmd_t cmd, uint32_t controller_id, uint32_t target_id,
	uint8_t type)
{
	cfga_err_t cfga_err;
	char *ap_id;
	int count = 0;

	ap_id = (char *)malloc(MAX_PATH_LEN);
	if (ap_id == NULL)
		return (ERR_NOMEM);

	if (type == 0) {
		(void) snprintf(ap_id, MAX_PATH_LEN, "c%d::dsk/c%dt%dd0",
		    controller_id, controller_id, target_id);
	} else
		(void) snprintf(ap_id, MAX_PATH_LEN, "c%d", controller_id);

	do {
		cfga_err = config_change_state(cmd, 1, &ap_id, "disable_rcm",
		    NULL, NULL, NULL, 0);
		count++;
	} while (cfga_err != CFGA_OK && count < 2);

	if (cfga_err != CFGA_OK) {
		free(ap_id);
		return (ERR_ARRAY_CONFIG);
	}

	free(ap_id);
	return (SUCCESS);
}

/*
 * Raid handle maintenance routines
 */
static int
raid_handle_init()
{
	int i;
	void *ptr;

	raid_handle_sys.handle_num += HANDLER_SLOTS;
	ptr = realloc(raid_handle_sys.handles,
	    raid_handle_sys.handle_num * sizeof (handle_attr_t));
	if (ptr == NULL)
		return (ERR_NOMEM);
	raid_handle_sys.handles = ptr;

	/* Clean up the new allocated handles */
	for (i = raid_handle_sys.handle_num - HANDLER_SLOTS;
	    i < raid_handle_sys.handle_num; ++i) {
		raid_handle_sys.handles[i].type = OBJ_TYPE_ALL;
		raid_handle_sys.handles[i].next = i + 1;
	}

	/* For the first time of allocation, set up the system object handle */
	if (raid_handle_sys.handle_num == HANDLER_SLOTS) {
		raid_handle_sys.handles[0].type = OBJ_TYPE_SYSTEM;
		raid_handle_sys.handles[0].next = 0;
		raid_handle_sys.unused = 1;
		raid_handle_sys.used = 0;
	}
	return (SUCCESS);
}

static void
raid_handle_fini()
{
	raid_obj_handle_t i;

	i = raid_handle_sys.used;

	/* Close all opened controllers */
	while (i != 0) {
		if ((raid_handle_sys.handles[i].type == OBJ_TYPE_CONTROLLER) &&
		    (raid_handle_sys.handles[i].fd != 0) &&
		    (raid_handle_sys.handles[i].raid_lib != NULL))
			raid_handle_sys.handles[i].raid_lib->close_controller(
			    raid_handle_sys.handles[i].controller_id, NULL);
		i = raid_handle_sys.handles[i].next;
	}

	/* Clean up handle space */
	raid_handle_sys.handle_num = 0;
	raid_handle_sys.unused = 0;
	raid_handle_sys.used = 0;
	free(raid_handle_sys.handles);
	raid_handle_sys.handles = NULL;
}

static raid_obj_handle_t
raid_handle_new(raid_obj_type_id_t type)
{
	int ret;

	if (raid_handle_sys.unused == raid_handle_sys.handle_num - 1) {
		ret = raid_handle_init();
		if (ret < SUCCESS)
			return (ret);
	}

	ret = raid_handle_sys.unused;
	raid_handle_sys.unused = raid_handle_sys.handles[ret].next;

	raid_handle_sys.handles[ret].next = raid_handle_sys.used;
	raid_handle_sys.used = ret;
	raid_handle_sys.handles[ret].type = type;

	return (ret);
}

static void
raid_handle_delete(raid_obj_handle_t handle)
{
	int i = raid_handle_sys.used, j = 0;

	if (handle == 0)
		return;

	while (i != 0 && i != handle) {
		j = i;
		i = raid_handle_sys.handles[i].next;
	}

	if (i == handle) {
		if (j != 0)
			raid_handle_sys.handles[j].next =
			    raid_handle_sys.handles[i].next;
		else
			raid_handle_sys.used =
			    raid_handle_sys.handles[i].next;

		raid_handle_sys.handles[i].type = OBJ_TYPE_ALL;
		raid_handle_sys.handles[i].next =
		    raid_handle_sys.unused;
		raid_handle_sys.unused = i;
	}
}

static void
raid_handle_delete_controller_comp(uint32_t controller_id)
{
	int i = raid_handle_sys.used, j;

	while (i != 0) {
		j = i;
		i = raid_handle_sys.handles[i].next;
		if ((raid_handle_sys.handles[j].controller_id ==
		    controller_id) &&
		    (raid_handle_sys.handles[j].type !=
		    OBJ_TYPE_CONTROLLER))
		raid_handle_delete(j);
	}
}

static raid_obj_id_t
raid_handle_to_obj(raid_obj_tab_t *raid_tab, raid_obj_handle_t handle)
{
	handle_attr_t *handle_attr;
	raid_obj_id_t obj_id;

	if (handle == OBJ_SYSTEM)
		return (OBJ_SYSTEM);

	handle_attr = raid_handle_sys.handles + handle;

	switch (handle_attr->type) {
	case	OBJ_TYPE_SYSTEM:
		return (OBJ_SYSTEM);
	case	OBJ_TYPE_CONTROLLER:
		obj_id = obj_locate_controller(raid_tab,
		    handle_attr->controller_id);
		break;
	case	OBJ_TYPE_ARRAY:
		obj_id = obj_locate_array(raid_tab,
		    handle_attr->controller_id, handle_attr->array_id);
		break;
	case	OBJ_TYPE_HSP:
		obj_id = obj_locate_hsp(raid_tab,
		    handle_attr->controller_id, handle_attr->disk_id,
		    handle_attr->array_id);
		break;
	case	OBJ_TYPE_DISK:
		obj_id = obj_locate_disk(raid_tab,
		    handle_attr->controller_id, handle_attr->disk_id);
		break;
	case	OBJ_TYPE_ARRAY_PART:
		obj_id = obj_locate_arraypart(raid_tab,
		    handle_attr->controller_id, handle_attr->array_id,
		    handle_attr->disk_id);
		break;
	case	OBJ_TYPE_DISK_SEG:
		obj_id = obj_locate_diskseg(raid_tab,
		    handle_attr->controller_id,
		    handle_attr->disk_id, handle_attr->seq_id);
		break;
	case	OBJ_TYPE_TASK:
		obj_id = obj_locate_task(raid_tab,
		    handle_attr->controller_id, handle_attr->task_id);
		break;
	default:
		return (ERR_DEVICE_INVALID);
	}

	if (obj_id < OBJ_NONE)
		return (obj_id);
	if (obj_id == OBJ_NONE)
		return (ERR_DEVICE_NOENT);

	(void) raid_obj_set_handle(raid_tab, obj_id, handle);
	return (obj_id);
}

static raid_obj_handle_t
raid_obj_to_handle(raid_obj_tab_t *raid_tab, raid_obj_id_t obj_id)
{
	raid_obj_id_t obj_id_backup = obj_id;
	raid_obj_type_id_t type;
	raid_obj_handle_t handle;
	controller_attr_t *controller_attr;
	array_attr_t *array_attr;
	hsp_attr_t *hsp_attr;
	disk_attr_t *disk_attr;
	arraypart_attr_t *arraypart_attr;
	diskseg_attr_t *diskseg_attr;
	task_attr_t *task_attr;

	if (obj_id == OBJ_SYSTEM)
		return (OBJ_SYSTEM);

	/* If the object mapped by a handle */
	handle = raid_obj_get_handle(raid_tab, obj_id);
	if (handle != 0)
		return (handle);

	/* Search for existing handles */
	for (handle = raid_handle_sys.used; handle != 0;
	    handle = raid_handle_sys.handles[handle].next)
		if (raid_handle_to_obj(raid_tab, handle) == obj_id)
			break;

	if (handle != 0)
		return (handle);

	/* Allocate new handle for this object */
	type = raid_obj_get_type(raid_tab, obj_id);
	handle = raid_handle_new(type);
	(void) raid_obj_set_handle(raid_tab, obj_id, handle);
	raid_handle_sys.handles[handle].type = type;

	switch (type) {
	case OBJ_TYPE_SYSTEM:
		break;
	case OBJ_TYPE_CONTROLLER:
		controller_attr = raid_obj_get_data_ptr(raid_tab, obj_id);
		raid_handle_sys.handles[handle].controller_id =
		    controller_attr->controller_id;
		break;
	case OBJ_TYPE_ARRAY:
		array_attr = raid_obj_get_data_ptr(raid_tab, obj_id);
		raid_handle_sys.handles[handle].array_id = array_attr->array_id;
		obj_id = obj_get_controller(raid_tab, obj_id);
		controller_attr = raid_obj_get_data_ptr(raid_tab, obj_id);
		raid_handle_sys.handles[handle].controller_id =
		    controller_attr->controller_id;
		break;
	case OBJ_TYPE_HSP:
		hsp_attr = raid_obj_get_data_ptr(raid_tab, obj_id);
		raid_handle_sys.handles[handle].array_id =
		    hsp_attr->associated_id;
		obj_id = raid_obj_get_container(raid_tab, obj_id);
		disk_attr = raid_obj_get_data_ptr(raid_tab, obj_id);
		raid_handle_sys.handles[handle].disk_id = disk_attr->disk_id;
		obj_id = obj_get_controller(raid_tab, obj_id);
		controller_attr = raid_obj_get_data_ptr(raid_tab, obj_id);
		raid_handle_sys.handles[handle].controller_id =
		    controller_attr->controller_id;
		break;
	case OBJ_TYPE_DISK:
		disk_attr = raid_obj_get_data_ptr(raid_tab, obj_id);
		raid_handle_sys.handles[handle].disk_id = disk_attr->disk_id;
		obj_id = obj_get_controller(raid_tab, obj_id);
		controller_attr = raid_obj_get_data_ptr(raid_tab, obj_id);
		raid_handle_sys.handles[handle].controller_id =
		    controller_attr->controller_id;
		break;
	case OBJ_TYPE_ARRAY_PART:
		arraypart_attr = raid_obj_get_data_ptr(raid_tab, obj_id);
		raid_handle_sys.handles[handle].disk_id =
		    arraypart_attr->disk_id;
		obj_id = raid_obj_get_container(raid_tab, obj_id);
		array_attr = raid_obj_get_data_ptr(raid_tab, obj_id);
		raid_handle_sys.handles[handle].array_id =
		    array_attr->array_id;
		obj_id = obj_get_controller(raid_tab, obj_id);
		controller_attr = raid_obj_get_data_ptr(raid_tab, obj_id);
		raid_handle_sys.handles[handle].controller_id =
		    controller_attr->controller_id;
		break;
	case OBJ_TYPE_DISK_SEG:
		diskseg_attr = raid_obj_get_data_ptr(raid_tab, obj_id);
		raid_handle_sys.handles[handle].seq_id = diskseg_attr->seq_no;
		obj_id = raid_obj_get_container(raid_tab, obj_id);
		disk_attr = raid_obj_get_data_ptr(raid_tab, obj_id);
		raid_handle_sys.handles[handle].disk_id =
		    disk_attr->disk_id;
		obj_id = obj_get_controller(raid_tab, obj_id);
		controller_attr = raid_obj_get_data_ptr(raid_tab, obj_id);
		raid_handle_sys.handles[handle].controller_id =
		    controller_attr->controller_id;
		break;
	case OBJ_TYPE_TASK:
		task_attr = raid_obj_get_data_ptr(raid_tab, obj_id);
		raid_handle_sys.handles[handle].task_id = task_attr->task_id;
		obj_id = obj_get_controller(raid_tab, obj_id);
		controller_attr = raid_obj_get_data_ptr(raid_tab, obj_id);
		raid_handle_sys.handles[handle].controller_id =
		    controller_attr->controller_id;
		break;
	default:
		return (ERR_DEVICE_INVALID);
	}

	(void) raid_obj_set_handle(raid_tab, obj_id_backup, handle);
	return (handle);
}

static raid_lib_t *
raid_obj_get_lib(raid_obj_tab_t *raid_tab, raid_obj_id_t obj_id)
{
	raid_obj_handle_t handle;
	controller_attr_t *attr;

	if (raid_obj_get_type(raid_tab, obj_id) != OBJ_TYPE_CONTROLLER)
		return (NULL);

	attr = raid_obj_get_data_ptr(raid_tab, obj_id);
	handle = raid_handle_sys.used;
	while (raid_handle_sys.handles[handle].type != OBJ_TYPE_CONTROLLER ||
	    raid_handle_sys.handles[handle].controller_id !=
	    attr->controller_id)
		handle = raid_handle_sys.handles[handle].next;

	if (handle == 0)
		return (NULL);

	return (raid_handle_sys.handles[handle].raid_lib);
}

static int
raid_obj_set_lib(raid_obj_tab_t *raid_tab, raid_obj_id_t obj_id,
	raid_lib_t *raid_lib)
{
	raid_obj_handle_t handle;
	controller_attr_t *attr;

	if (raid_obj_get_type(raid_tab, obj_id) != OBJ_TYPE_CONTROLLER)
		return (ERR_DEVICE_TYPE);

	attr = raid_obj_get_data_ptr(raid_tab, obj_id);
	handle = raid_handle_sys.used;
	while (raid_handle_sys.handles[handle].type != OBJ_TYPE_CONTROLLER ||
	    raid_handle_sys.handles[handle].controller_id !=
	    attr->controller_id)
		handle = raid_handle_sys.handles[handle].next;

	if (handle == 0)
		return (ERR_DEVICE_NOENT);

	raid_handle_sys.handles[handle].raid_lib = raid_lib;
	return (SUCCESS);
}

static int
raid_obj_get_fd(raid_obj_tab_t *raid_tab, raid_obj_id_t obj_id)
{
	raid_obj_handle_t handle;
	controller_attr_t *attr;

	if (raid_obj_get_type(raid_tab, obj_id) != OBJ_TYPE_CONTROLLER)
		return (ERR_DEVICE_TYPE);

	attr = raid_obj_get_data_ptr(raid_tab, obj_id);
	handle = raid_handle_sys.used;
	while (raid_handle_sys.handles[handle].type != OBJ_TYPE_CONTROLLER ||
	    raid_handle_sys.handles[handle].controller_id !=
	    attr->controller_id)
		handle = raid_handle_sys.handles[handle].next;

	if (handle == 0)
		return (ERR_DEVICE_NOENT);

	return (raid_handle_sys.handles[handle].fd);
}

static int
raid_obj_set_fd(raid_obj_tab_t *raid_tab, raid_obj_id_t obj_id, int fd)
{
	raid_obj_handle_t handle;
	controller_attr_t *attr;

	if (raid_obj_get_type(raid_tab, obj_id) != OBJ_TYPE_CONTROLLER)
		return (ERR_DEVICE_TYPE);

	attr = raid_obj_get_data_ptr(raid_tab, obj_id);
	handle = raid_handle_sys.used;
	while (raid_handle_sys.handles[handle].type != OBJ_TYPE_CONTROLLER ||
	    raid_handle_sys.handles[handle].controller_id !=
	    attr->controller_id)
		handle = raid_handle_sys.handles[handle].next;

	if (handle == 0)
		return (ERR_DEVICE_NOENT);

	raid_handle_sys.handles[handle].fd = fd;
	return (SUCCESS);
}

/*
 * Raid object maintenance routines
 */
static int
obj_scan_comp(raid_obj_tab_t *raid_tab, raid_obj_id_t obj_id)
{
	raid_obj_status_t status;
	raid_obj_type_id_t type;
	int ret, i, obj_type_cnt, comp_num;
	raid_obj_id_t *comp_list;

	status = raid_obj_get_status(raid_tab, obj_id);
	if (status < SUCCESS)
		return (status);

	if (status & OBJ_STATUS_SCANCOMP)
		return (SUCCESS);

	type = raid_obj_get_type(raid_tab, obj_id);
	if (type < OBJ_TYPE_SYSTEM || type > OBJ_TYPE_ALL)
		return (ERR_DEVICE_INVALID);

	for (obj_type_cnt = OBJ_SYSTEM; obj_type_cnt < OBJ_TYPE_ALL;
	    ++obj_type_cnt) {
		if (raid_obj_op_sys[type].compnum != NULL)
			comp_num = raid_obj_op_sys[type].compnum(
			    raid_tab, obj_id, obj_type_cnt);
		else
			comp_num = 0;

		if (comp_num < SUCCESS)
			return (comp_num);
		if (comp_num == 0)
			continue;

		comp_list = calloc(comp_num, sizeof (raid_obj_id_t));
		if (comp_list == NULL)
			return (ERR_NOMEM);

		for (i = 0; i < comp_num; ++i) {
			*(comp_list + i) = raid_obj_create(raid_tab,
			    obj_type_cnt);
			if (*(comp_list + i) < SUCCESS) {
				ret = *(comp_list + i);
				free(comp_list);
				return (ret);
			}

			(void) raid_obj_clear_status(raid_tab,
			    *(comp_list + i), OBJ_STATUS_CMD_CLEAN);
			(void) raid_obj_add_org(raid_tab, *(comp_list + i),
			    obj_id);
		}

		if (raid_obj_op_sys[type].complist != NULL)
			raid_obj_op_sys[type].complist(raid_tab,
			    obj_id, comp_num, comp_list, obj_type_cnt);
		free(comp_list);
	}

	(void) raid_obj_set_status(raid_tab, obj_id, OBJ_STATUS_SCANCOMP);
	return (SUCCESS);
}

static int
obj_rescan(raid_obj_tab_t *raid_tab)
{
	int ret;

	raid_obj_tab_destroy(raid_tab);

	if (raid_obj_tab_create(raid_tab, HASH_SLOTS) != SUCCESS)
		return (ERR_NOMEM);

	if ((ret = raid_obj_create_system_obj(raid_tab)) != SUCCESS) {
		raid_obj_tab_destroy(raid_tab);
		return (ret);
	}

	return (SUCCESS);
}

static raid_obj_id_t
obj_get_comp(raid_obj_tab_t *raid_tab, raid_obj_id_t obj_id,
	raid_obj_type_id_t obj_type)
{
	raid_obj_id_t id;
	raid_obj_type_id_t type;
	raid_obj_status_t status;
	int ret;

	if ((obj_type < OBJ_TYPE_SYSTEM) || (obj_type > OBJ_TYPE_ALL))
		return (ERR_DEVICE_TYPE);

	status = raid_obj_get_status(raid_tab, obj_id);
	if (status < SUCCESS)
		return (status);

	if (!(status & OBJ_STATUS_SCANCOMP)) {
		ret = obj_scan_comp(raid_tab, obj_id);
		if (ret < SUCCESS)
			return (ret);
	}

	id = raid_obj_get_comp(raid_tab, obj_id);
	if (id <= OBJ_NONE)
		return (id);

	type = raid_obj_get_type(raid_tab, id);
	if (type < OBJ_TYPE_SYSTEM)
		return (type);

	if (type == obj_type)
		return (id);

	while (id > OBJ_NONE) {
		id = raid_obj_get_sibling(raid_tab, id);
		if (id <= OBJ_NONE)
			return (id);

		type = raid_obj_get_type(raid_tab, id);
		if (type < OBJ_TYPE_SYSTEM)
			return (type);

		if (type == obj_type)
			break;
	};

	return (id);
}

static raid_obj_id_t
obj_get_sibling(raid_obj_tab_t *raid_tab, raid_obj_id_t obj_id)
{
	raid_obj_id_t id;
	raid_obj_type_id_t type, obj_type;

	id = obj_id;
	obj_type = raid_obj_get_type(raid_tab, id);
	if (obj_type < OBJ_TYPE_SYSTEM)
		return (obj_type);

	do {
		id = raid_obj_get_sibling(raid_tab, id);
		if (id < OBJ_NONE)
			return (id);

		type = raid_obj_get_type(raid_tab, id);
		if (type < OBJ_TYPE_SYSTEM)
			return (type);
	} while ((type != obj_type) && (id != OBJ_NONE));

	return (id);
}

static int
obj_get_attr(raid_obj_tab_t *raid_tab, raid_obj_id_t obj_id, void **data)
{
	raid_obj_type_id_t type;
	raid_obj_status_t status;
	void *attr;
	int ret = SUCCESS;

	status = raid_obj_get_status(raid_tab, obj_id);
	if (status < SUCCESS)
		return (status);

	type = raid_obj_get_type(raid_tab, obj_id);
	if (type < OBJ_TYPE_SYSTEM)
		return (type);

	if (!(status & OBJ_STATUS_OPENED)) {
		if (raid_obj_op_sys[type].get_attr == NULL)
			(void) raid_obj_set_status(raid_tab, obj_id,
			    OBJ_STATUS_OPENED);
		else
			ret = raid_obj_op_sys[type].get_attr(raid_tab, obj_id);
	}
	if (ret < SUCCESS)
		return (ret);

	attr = raid_obj_get_data_ptr(raid_tab, obj_id);
	if (attr == NULL && type != OBJ_TYPE_SYSTEM)
		return (ERR_DEVICE_INVALID);

	*data = attr;
	return (SUCCESS);
}

static raid_obj_id_t
obj_locate_controller(raid_obj_tab_t *raid_tab, uint32_t controller_id)
{
	raid_obj_id_t obj_id;
	controller_attr_t *attr;

	obj_id = obj_get_comp(raid_tab, OBJ_SYSTEM, OBJ_TYPE_CONTROLLER);
	if (obj_id <= OBJ_NONE)
		return (obj_id);

	do {
		attr = raid_obj_get_data_ptr(raid_tab, obj_id);
		if (attr == NULL)
			return (ERR_DEVICE_INVALID);

		if (attr->controller_id == controller_id)
			break;
	} while ((obj_id = obj_get_sibling(raid_tab, obj_id)) != OBJ_NONE);

	return (obj_id);
}

static raid_obj_id_t
obj_locate_array(raid_obj_tab_t *raid_tab, uint32_t controller_id,
	uint32_t array_id)
{
	raid_obj_id_t obj_id;

	obj_id = obj_locate_controller(raid_tab, controller_id);
	if (obj_id < OBJ_NONE)
		return (obj_id);

	obj_id = obj_locate_array_recur(raid_tab, obj_id, array_id);

	return (obj_id);
}

static raid_obj_id_t
obj_locate_array_recur(raid_obj_tab_t *raid_tab,
	raid_obj_id_t container_obj_id, uint32_t array_id)
{
	raid_obj_id_t obj_id, ret;
	array_attr_t *attr;

	obj_id = obj_get_comp(raid_tab, container_obj_id, OBJ_TYPE_ARRAY);
	if (obj_id <= OBJ_NONE)
		return (obj_id);

	do {
		attr = raid_obj_get_data_ptr(raid_tab, obj_id);
		if (attr == NULL)
			return (ERR_DEVICE_INVALID);

		if (attr->array_id == array_id)
			break;

		ret = obj_locate_array_recur(raid_tab, obj_id, array_id);
		if (ret != OBJ_NONE)
			return (ret);

	} while ((obj_id = obj_get_sibling(raid_tab, obj_id)) > OBJ_NONE);

	return (obj_id);
}

static raid_obj_id_t
obj_locate_hsp(raid_obj_tab_t *raid_tab, uint32_t controller_id,
	uint32_t disk_id, uint32_t array_id)
{
	raid_obj_id_t obj_id;
	hsp_attr_t *hsp_attr;

	obj_id = obj_locate_disk(raid_tab, controller_id, disk_id);
	if (obj_id <= OBJ_NONE)
		return (obj_id);

	obj_id = obj_get_comp(raid_tab, obj_id, OBJ_TYPE_HSP);
	if (obj_id <= OBJ_NONE)
		return (obj_id);

	do {
		(void) obj_get_attr(raid_tab, obj_id, (void **)(&hsp_attr));
		if (hsp_attr->associated_id == array_id)
			break;

		obj_id = obj_get_sibling(raid_tab, obj_id);
		if (obj_id < OBJ_NONE)
			return (obj_id);
	} while (obj_id > OBJ_NONE);

	return (obj_id);
}

static raid_obj_id_t
obj_locate_disk(raid_obj_tab_t *raid_tab, uint32_t controller_id,
	uint32_t disk_id)
{
	raid_obj_id_t obj_id;
	disk_attr_t *attr;

	obj_id = obj_locate_controller(raid_tab, controller_id);
	if (obj_id <= OBJ_NONE)
		return (obj_id);

	obj_id = obj_get_comp(raid_tab, obj_id, OBJ_TYPE_DISK);
	if (obj_id <= OBJ_NONE)
		return (obj_id);

	do {
		attr = raid_obj_get_data_ptr(raid_tab, obj_id);
		if (attr == NULL)
			return (ERR_DEVICE_INVALID);

		if (attr->disk_id == disk_id)
			break;
	} while ((obj_id = obj_get_sibling(raid_tab, obj_id)) > OBJ_NONE);

	return (obj_id);
}

static raid_obj_id_t
obj_locate_arraypart(raid_obj_tab_t *raid_tab, uint32_t controller_id,
	uint32_t array_id, uint32_t disk_id)
{
	raid_obj_id_t obj_id;

	arraypart_attr_t *attr;

	obj_id = obj_locate_array(raid_tab, controller_id, array_id);
	if (obj_id <= OBJ_NONE)
		return (obj_id);

	obj_id = obj_get_comp(raid_tab, obj_id, OBJ_TYPE_ARRAY_PART);
	if (obj_id <= OBJ_NONE)
		return (obj_id);

	do {
		attr = raid_obj_get_data_ptr(raid_tab, obj_id);
		if (attr == NULL)
			return (ERR_DEVICE_INVALID);

		if (attr->disk_id == disk_id)
			break;
	} while ((obj_id = obj_get_sibling(raid_tab, obj_id)) >
	    OBJ_NONE);

	return (obj_id);
}

static raid_obj_id_t
obj_locate_diskseg(raid_obj_tab_t *raid_tab, uint32_t controller_id,
	uint32_t disk_id, uint32_t seq_no)
{
	raid_obj_id_t obj_id;
	diskseg_attr_t *attr;

	obj_id = obj_locate_disk(raid_tab, controller_id, disk_id);
	if (obj_id <= OBJ_NONE)
		return (obj_id);

	obj_id = obj_get_comp(raid_tab, obj_id, OBJ_TYPE_DISK_SEG);
	if (obj_id <= OBJ_NONE)
		return (obj_id);

	do {
		attr = raid_obj_get_data_ptr(raid_tab, obj_id);
		if (attr == NULL)
			return (ERR_DEVICE_INVALID);

		if (attr->seq_no == seq_no)
			break;
	} while ((obj_id = obj_get_sibling(raid_tab, obj_id)) > OBJ_NONE);

	return (obj_id);
}

static raid_obj_id_t
obj_locate_task(raid_obj_tab_t *raid_tab, uint32_t controller_id,
	uint32_t task_id)
{
	raid_obj_id_t obj_id, obj_id2, task_obj_id;
	task_attr_t *attr;

	obj_id = obj_locate_controller(raid_tab, controller_id);
	if (obj_id <= OBJ_NONE)
		return (obj_id);

	obj_id = obj_get_comp(raid_tab, obj_id, OBJ_TYPE_ARRAY);
	if (obj_id < OBJ_NONE)
		return (obj_id);

	do {
		obj_id2 = obj_get_comp(raid_tab, obj_id, OBJ_TYPE_ARRAY);
		while (obj_id2 != OBJ_NONE) {
			task_obj_id = obj_get_comp(raid_tab, obj_id2,
			    OBJ_TYPE_TASK);

			if (task_obj_id < OBJ_NONE)
				return (task_obj_id);

			if (task_obj_id == OBJ_NONE) {
				obj_id2 = obj_get_sibling(raid_tab, obj_id2);
				continue;
			}

			attr = raid_obj_get_data_ptr(raid_tab, task_obj_id);
			if (attr == NULL)
				return (ERR_DEVICE_INVALID);

			if (attr->task_id == task_id)
				return (task_obj_id);

			obj_id2 = obj_get_sibling(raid_tab, obj_id2);
		}

		task_obj_id = obj_get_comp(raid_tab, obj_id, OBJ_TYPE_TASK);
		if (task_obj_id < OBJ_NONE)
			return (task_obj_id);

		if (task_obj_id == OBJ_NONE)
			continue;

		attr = raid_obj_get_data_ptr(raid_tab, task_obj_id);
		if (attr == NULL)
			return (ERR_DEVICE_INVALID);

		if (attr->task_id == task_id)
			return (task_obj_id);
	} while ((obj_id = obj_get_sibling(raid_tab, obj_id)) > OBJ_NONE);

	if (obj_id < OBJ_NONE)
		return (obj_id);

	obj_id = obj_locate_controller(raid_tab, controller_id);
	obj_id = obj_get_comp(raid_tab, obj_id, OBJ_TYPE_DISK);
	if (obj_id < OBJ_NONE)
		return (obj_id);

	do {
		task_obj_id = obj_get_comp(raid_tab, obj_id, OBJ_TYPE_TASK);
		if (task_obj_id < OBJ_NONE)
			return (task_obj_id);

		if (task_obj_id == OBJ_NONE)
			continue;

		attr = raid_obj_get_data_ptr(raid_tab, task_obj_id);
		if (attr == NULL)
			return (ERR_DEVICE_INVALID);

		if (attr->task_id == task_id)
			return (task_obj_id);
	} while ((obj_id = obj_get_sibling(raid_tab, obj_id)) > OBJ_NONE);

	return (obj_id);

}

static raid_obj_id_t
obj_get_controller(raid_obj_tab_t *raid_tab, raid_obj_id_t obj_id)
{
	raid_obj_id_t id = obj_id;

	while (raid_obj_get_type(raid_tab, id) != OBJ_TYPE_CONTROLLER) {
		id = raid_obj_get_container(raid_tab, id);
		if ((id == OBJ_SYSTEM) || (id < OBJ_NONE))
			return (ERR_DEVICE_INVALID);
	}

	return (id);
}

/*
 * Raid object operation routines
 */
static int
obj_sys_compnum(raid_obj_tab_t *raid_tab, raid_obj_id_t obj_id,
	raid_obj_type_id_t comp_type)
{
	DIR *dir;
	struct dirent *dp;
	int num = 0;

	if ((raid_obj_get_type(raid_tab, obj_id) != OBJ_TYPE_SYSTEM))
		return (ERR_DEVICE_TYPE);

	if (comp_type != OBJ_TYPE_CONTROLLER)
		return (0);

	if ((dir = opendir(CFGDIR)) == NULL)
		return (ERR_DRIVER_NOT_FOUND);

	while ((dp = readdir(dir)) != NULL) {
		uint32_t controller_id;
		char path[MAX_PATH_LEN];

		if (strcmp(dp->d_name, ".") == 0 ||
		    strcmp(dp->d_name, "..") == 0)
			continue;

		if (sscanf(dp->d_name, "c%u", &controller_id) != 1)
			continue;

		if (controller_id_to_path(controller_id, path) == SUCCESS)
			++ num;
	}

	(void) closedir(dir);
	return (num);
}

static int
obj_sys_complist(raid_obj_tab_t *raid_tab, raid_obj_id_t obj_id,
	int num, raid_obj_id_t *comp_list, raid_obj_type_id_t comp_type)
{
	DIR *dir;
	struct dirent *dp;
	controller_attr_t *attr;
	uint32_t controller_id;
	uint32_t *tmplist;
	char path[MAX_PATH_LEN];
	int i = 0;

	if (raid_obj_get_type(raid_tab, obj_id) != OBJ_TYPE_SYSTEM)
		return (ERR_DEVICE_TYPE);
	if ((num <= 0) || (comp_list == NULL))
		return (ERR_OP_ILLEGAL);

	if (comp_type != OBJ_TYPE_CONTROLLER)
		return (0);

	if ((dir = opendir(CFGDIR)) == NULL)
		return (ERR_DRIVER_NOT_FOUND);
	tmplist = calloc(num, sizeof (uint32_t));
	if (tmplist == NULL) {
		return (ERR_NOMEM);
	}
	while ((dp = readdir(dir)) != NULL) {
		if (strcmp(dp->d_name, ".") == 0 ||
		    strcmp(dp->d_name, "..") == 0)
			continue;

		if (sscanf(dp->d_name, "c%u", &controller_id) != 1)
			continue;

		if (controller_id_to_path(controller_id, path) == SUCCESS) {
			tmplist[i] = controller_id;
			++ i;
		}
	}
	qsort((void *)tmplist, num, sizeof (uint32_t), intcompare);
	for (i = 0; i < num; i++) {
		attr = raid_obj_get_data_ptr(raid_tab,
		    *(comp_list + i));

		if (attr == NULL) {
			free(tmplist);
			return (ERR_DEVICE_INVALID);
		}

		attr->controller_id = tmplist[i];
	}
	free(tmplist);
	(void) closedir(dir);
	return (SUCCESS);
}

static int
obj_controller_compnum(raid_obj_tab_t *raid_tab, raid_obj_id_t obj_id,
	raid_obj_type_id_t comp_type)
{
	raid_lib_t *raid_lib;
	int ret = SUCCESS, fd;
	controller_attr_t *ctl_attrp;

	if (raid_obj_get_type(raid_tab, obj_id) != OBJ_TYPE_CONTROLLER)
		return (ERR_DEVICE_TYPE);

	if ((comp_type != OBJ_TYPE_ARRAY) && (comp_type != OBJ_TYPE_DISK))
		return (0);

	raid_lib = raid_obj_get_lib(raid_tab, obj_id);
	fd = raid_obj_get_fd(raid_tab, obj_id);
	ctl_attrp = raid_obj_get_data_ptr(raid_tab, obj_id);
	if ((raid_lib == NULL) || (ctl_attrp == NULL) || (fd == 0))
		return (ERR_DRIVER_CLOSED);

	ret = raid_lib->compnum(ctl_attrp->controller_id, 0,
	    OBJ_TYPE_CONTROLLER, comp_type);

	return (ret);
}

static int
obj_controller_complist(raid_obj_tab_t *raid_tab, raid_obj_id_t obj_id,
	int comp_num, raid_obj_id_t *comp_list, raid_obj_type_id_t comp_type)
{
	raid_lib_t *raid_lib;
	controller_attr_t *ctl_attrp;
	int ret, i, fd;
	uint32_t *ids;

	if (raid_obj_get_type(raid_tab, obj_id) != OBJ_TYPE_CONTROLLER)
		return (ERR_DEVICE_TYPE);

	if ((comp_type != OBJ_TYPE_ARRAY) && (comp_type != OBJ_TYPE_DISK))
		return (0);

	if ((comp_num <= 0) || (comp_list == NULL))
		return (ERR_OP_ILLEGAL);

	for (i = 0; i < comp_num; ++i)
		if (raid_obj_get_type(raid_tab, *(comp_list + i)) !=
		    comp_type)
			return (ERR_DEVICE_TYPE);

	raid_lib = raid_obj_get_lib(raid_tab, obj_id);
	ctl_attrp = raid_obj_get_data_ptr(raid_tab, obj_id);
	fd = raid_obj_get_fd(raid_tab, obj_id);
	if ((raid_lib == NULL) || (ctl_attrp == NULL)|| (fd == 0))
		return (ERR_DRIVER_CLOSED);

	ids = malloc(comp_num * sizeof (uint32_t));
	if (ids == NULL)
		return (ERR_NOMEM);

	ret = raid_lib->complist(ctl_attrp->controller_id, 0,
	    OBJ_TYPE_CONTROLLER, comp_type, comp_num, ids);
	if (ret < SUCCESS) {
		free(ids);
		return (ret);
	}
	qsort((void *)ids, comp_num, sizeof (uint32_t), intcompare);
	for (i = 0; i < comp_num; ++ i) {
		array_attr_t *array_attr;
		disk_attr_t *disk_attr;
		void *attr_buf;

		attr_buf = raid_obj_get_data_ptr(raid_tab, *(comp_list + i));
		if (attr_buf == NULL) {
			free(ids);
			return (ERR_DEVICE_INVALID);
		}

		switch (comp_type) {
		case OBJ_TYPE_ARRAY:
			array_attr = attr_buf;
			array_attr->array_id = *(ids + i);
			break;
		case OBJ_TYPE_DISK:
			disk_attr = attr_buf;
			disk_attr->disk_id = *(ids + i);
			break;
		default:
			free(ids);
			return (ERR_DEVICE_INVALID);
		}
	}

	free(ids);
	return (SUCCESS);
}

static int
obj_controller_get_attr(raid_obj_tab_t *raid_tab, raid_obj_id_t obj_id)
{
	controller_attr_t *attr;
	raid_lib_t *raid_lib;
	int ret = SUCCESS, fd;

	if (raid_obj_get_type(raid_tab, obj_id) != OBJ_TYPE_CONTROLLER)
		return (ERR_DEVICE_TYPE);

	if (raid_obj_get_status(raid_tab, obj_id) & OBJ_STATUS_OPENED)
		return (SUCCESS);

	attr = raid_obj_get_data_ptr(raid_tab, obj_id);
	if (attr == NULL)
		return (ERR_DEVICE_INVALID);

	raid_lib = raid_obj_get_lib(raid_tab, obj_id);
	fd = raid_obj_get_fd(raid_tab, obj_id);

	/*
	 * For a controller, even it's not opened, we can still
	 * get the driver name
	 */

	if (fd == 0)
		return (SUCCESS);

	if (raid_lib == NULL) {
		return (SUCCESS);
	}

	ret = raid_lib->get_attr(attr->controller_id, OBJ_ATTR_NONE,
	    OBJ_ATTR_NONE, OBJ_TYPE_CONTROLLER, attr);
	if (ret < SUCCESS)
		return (ret);

	(void) raid_obj_set_status(raid_tab, obj_id, OBJ_STATUS_OPENED);

	return (ret);
}

static int
obj_controller_act(raid_obj_tab_t *raid_tab, raid_obj_id_t obj_id,
	uint32_t sub_cmd, void *prop_list, char **plugin_err_str)
{
	controller_attr_t *attr;
	raid_lib_t *raid_lib;
	int ret, fd;

	if (raid_obj_get_type(raid_tab, obj_id) != OBJ_TYPE_CONTROLLER)
		return (ERR_DEVICE_TYPE);

	attr = raid_obj_get_data_ptr(raid_tab, obj_id);

	raid_lib = raid_obj_get_lib(raid_tab, obj_id);
	fd = raid_obj_get_fd(raid_tab, obj_id);

	switch (sub_cmd) {
	case ACT_CONTROLLER_OPEN:
		/* Check if already opened */

		if (fd > 0)
			return (SUCCESS);

		/* Check if plugin is already attached */
		if (raid_lib == NULL) {
			raid_lib = raid_find_lib(raid_tab, obj_id);
			if (raid_lib == NULL)
				return (ERR_DRIVER_NOT_FOUND);
		}

		ret = raid_lib->open_controller(attr->controller_id,
		    plugin_err_str);
		if (ret == SUCCESS) {
			(void) raid_obj_set_lib(raid_tab, obj_id, raid_lib);
			(void) raid_obj_set_fd(raid_tab, obj_id, 1);
		}
		break;
	case ACT_CONTROLLER_CLOSE:

		if (fd <= 0)
			return (SUCCESS);

		if (raid_lib == NULL) {
			return (SUCCESS);
		}
		ret = raid_lib->close_controller(attr->controller_id,
		    plugin_err_str);
		if (ret == SUCCESS) {
			(void) raid_obj_set_fd(raid_tab, obj_id, 0);
			(void) raid_obj_set_lib(raid_tab, obj_id, NULL);
			raid_handle_delete_controller_comp(attr->controller_id);
		}
		break;
	case ACT_CONTROLLER_FLASH_FW:
		{
			char		*filebuf;
			int		image_fd;
			uint32_t	size;
			struct stat	statbuf;

			if (prop_list == NULL)
				return (ERR_OP_ILLEGAL);

			/* Open firmware image file */
			image_fd = open((const char *)prop_list,
			    O_RDONLY | O_NDELAY);
			if (image_fd == -1)
				return (ERR_OP_FAILED);

			if (fstat(image_fd, &statbuf) != 0) {
				(void) close(image_fd);
				return (ERR_OP_FAILED);
			}

			filebuf = malloc(statbuf.st_size);
			if (filebuf == NULL) {
				(void) close(image_fd);
				return (ERR_NOMEM);
			}

			size = read(image_fd, filebuf, statbuf.st_size);
			if (size != statbuf.st_size) {
				(void) close(image_fd);
				free(filebuf);
				return (ERR_OP_FAILED);
			}

			if (fd <= 0) {
				(void) close(image_fd);
				free(filebuf);
				return (ERR_DRIVER_CLOSED);
			}

			if (raid_lib == NULL) {
				(void) close(image_fd);
				free(filebuf);
				return (ERR_DRIVER_CLOSED);
			}
			if (raid_lib->flash_fw == NULL) {
				(void) close(image_fd);
				free(filebuf);
				return (ERR_OP_NO_IMPL);
			}

			ret = raid_lib->flash_fw(attr->controller_id,
			    filebuf, size, plugin_err_str);
		}
		break;
	default:
		return (ERR_OP_ILLEGAL);
	}

	return (ret);
}

static int
obj_array_compnum(raid_obj_tab_t *raid_tab, raid_obj_id_t obj_id,
	raid_obj_type_id_t comp_type)
{
	array_attr_t *attr;
	controller_attr_t *ctl_attrp;
	raid_obj_id_t controller_obj_id;
	raid_lib_t *raid_lib;
	int ret = SUCCESS, fd;

	if (raid_obj_get_type(raid_tab, obj_id) != OBJ_TYPE_ARRAY)
		return (ERR_DEVICE_TYPE);

	if (comp_type != OBJ_TYPE_ARRAY_PART &&
	    comp_type != OBJ_TYPE_ARRAY &&
	    comp_type != OBJ_TYPE_TASK)
		return (0);

	attr = raid_obj_get_data_ptr(raid_tab, obj_id);
	if (attr == NULL)
		return (ERR_DEVICE_INVALID);

	controller_obj_id = obj_get_controller(raid_tab, obj_id);
	if (controller_obj_id < OBJ_NONE)
		return (ERR_DEVICE_INVALID);

	ctl_attrp = raid_obj_get_data_ptr(raid_tab, controller_obj_id);
	if (ctl_attrp == NULL) {
		return (ERR_DEVICE_INVALID);
	}

	raid_lib = raid_obj_get_lib(raid_tab, controller_obj_id);
	fd = raid_obj_get_fd(raid_tab, controller_obj_id);
	if ((raid_lib == NULL) || (fd == 0))
		return (ERR_DRIVER_CLOSED);

	ret = raid_lib->compnum(ctl_attrp->controller_id, attr->array_id,
	    OBJ_TYPE_ARRAY, comp_type);

	return (ret);
}

static int
obj_array_complist(raid_obj_tab_t *raid_tab, raid_obj_id_t obj_id,
	int comp_num, raid_obj_id_t *comp_list, raid_obj_type_id_t comp_type)
{
	array_attr_t *attr;
	controller_attr_t *ctl_attrp;
	raid_obj_id_t controller_obj_id;
	raid_lib_t *raid_lib;
	int ret, i, fd;
	uint32_t *ids;

	if (raid_obj_get_type(raid_tab, obj_id) != OBJ_TYPE_ARRAY)
		return (ERR_DEVICE_TYPE);

	if (comp_type != OBJ_TYPE_ARRAY_PART &&
	    comp_type != OBJ_TYPE_ARRAY &&
	    comp_type != OBJ_TYPE_TASK)
		return (0);

	if (comp_num <= 0 || comp_list == NULL)
		return (ERR_OP_ILLEGAL);

	for (i = 0; i < comp_num; ++i)
		if (raid_obj_get_type(raid_tab, *(comp_list + i)) !=
		    comp_type)
			return (ERR_DEVICE_TYPE);

	attr = raid_obj_get_data_ptr(raid_tab, obj_id);
	if (attr == NULL)
		return (ERR_DEVICE_INVALID);

	controller_obj_id = obj_get_controller(raid_tab, obj_id);
	if (controller_obj_id < OBJ_NONE)
		return (ERR_DEVICE_INVALID);

	ctl_attrp = raid_obj_get_data_ptr(raid_tab, controller_obj_id);
	if (ctl_attrp == NULL) {
		return (ERR_DEVICE_INVALID);
	}

	raid_lib = raid_obj_get_lib(raid_tab, controller_obj_id);
	fd = raid_obj_get_fd(raid_tab, controller_obj_id);
	if ((raid_lib == NULL) || (fd == 0))
		return (ERR_DRIVER_CLOSED);

	ids = malloc(comp_num * sizeof (uint32_t));
	if (ids == NULL)
		return (ERR_NOMEM);

	ret = raid_lib->complist(ctl_attrp->controller_id,
	    attr->array_id, OBJ_TYPE_ARRAY, comp_type, comp_num, ids);

	if (ret < SUCCESS) {
		free(ids);
		return (ret);
	}

	for (i = 0; i < comp_num; ++ i) {
		array_attr_t *array_attr;
		arraypart_attr_t *arraypart_attr;
		task_attr_t *task_attr;
		void *attr_buf;

		attr_buf = raid_obj_get_data_ptr(raid_tab, *(comp_list + i));
		if (attr_buf == NULL) {
			free(ids);
			return (ERR_DEVICE_INVALID);
		}

		switch (comp_type) {
		case OBJ_TYPE_ARRAY:
			array_attr = attr_buf;
			array_attr->array_id = *(ids + i);
			break;
		case OBJ_TYPE_ARRAY_PART:
			arraypart_attr = attr_buf;
			arraypart_attr->disk_id = *(ids + i);
			break;
		case OBJ_TYPE_TASK:
			task_attr = attr_buf;
			task_attr->task_id = *(ids + i);
			break;
		default:
			free(ids);
			return (ERR_DEVICE_INVALID);
		}
	}


	free(ids);
	return (ret);
}

static int
obj_array_get_attr(raid_obj_tab_t *raid_tab, raid_obj_id_t obj_id)
{
	array_attr_t *attr;
	controller_attr_t *ctl_attrp;
	raid_lib_t *raid_lib;
	int ret = SUCCESS, fd;
	raid_obj_id_t controller_obj_id;

	if (raid_obj_get_type(raid_tab, obj_id) != OBJ_TYPE_ARRAY)
		return (ERR_DEVICE_TYPE);

	if (raid_obj_get_status(raid_tab, obj_id) & OBJ_STATUS_OPENED)
		return (SUCCESS);

	attr = raid_obj_get_data_ptr(raid_tab, obj_id);
	if (attr == NULL)
		return (ERR_DEVICE_INVALID);

	controller_obj_id = obj_get_controller(raid_tab, obj_id);
	if (controller_obj_id < OBJ_NONE)
		return (ERR_DEVICE_INVALID);

	ctl_attrp = raid_obj_get_data_ptr(raid_tab, controller_obj_id);
	if (ctl_attrp == NULL) {
		return (ERR_DEVICE_INVALID);
	}

	raid_lib = raid_obj_get_lib(raid_tab, controller_obj_id);
	fd = raid_obj_get_fd(raid_tab, controller_obj_id);
	if ((raid_lib == NULL) || (fd == 0))
		return (ERR_DRIVER_CLOSED);

	ret = raid_lib->get_attr(ctl_attrp->controller_id,
	    attr->array_id, 0, OBJ_TYPE_ARRAY, attr);

	if (ret < SUCCESS)
		return (ret);

	(void) raid_obj_set_status(raid_tab, obj_id, OBJ_STATUS_OPENED);

	return (ret);
}

static int
obj_array_set_attr(raid_obj_tab_t *raid_tab, raid_obj_id_t obj_id,
	uint32_t sub_cmd, uint32_t *value, char **plugin_err_str)
{
	array_attr_t *attr;
	controller_attr_t *ctl_attrp;
	raid_lib_t *raid_lib;
	int ret = SUCCESS, fd;
	raid_obj_id_t controller_obj_id;

	if (raid_obj_get_type(raid_tab, obj_id) != OBJ_TYPE_ARRAY)
		return (ERR_DEVICE_TYPE);

	switch (sub_cmd) {
	case SET_CACHE_WR_PLY:
		if (*value != CACHE_WR_OFF &&
		    *value != CACHE_WR_ON)
			return (ERR_OP_ILLEGAL);
		break;
	case SET_CACHE_RD_PLY:
		if (*value != CACHE_RD_OFF &&
		    *value != CACHE_RD_ON)
			return (ERR_OP_ILLEGAL);
		break;
	default:
		return (ERR_OP_ILLEGAL);
	}

	(void) obj_get_attr(raid_tab, obj_id, (void **)(&attr));

	controller_obj_id = obj_get_controller(raid_tab, obj_id);
	if (controller_obj_id < OBJ_NONE)
		return (ERR_DEVICE_INVALID);

	ctl_attrp = raid_obj_get_data_ptr(raid_tab, controller_obj_id);
	if (ctl_attrp == NULL) {
		return (ERR_DEVICE_INVALID);
	}

	raid_lib = raid_obj_get_lib(raid_tab, controller_obj_id);
	fd = raid_obj_get_fd(raid_tab, controller_obj_id);
	if ((raid_lib == NULL) || (fd == 0))
		return (ERR_DRIVER_CLOSED);

	if (raid_lib->set_attr == NULL)
		return (ERR_OP_NO_IMPL);

	ret = raid_lib->set_attr(ctl_attrp->controller_id,
	    attr->array_id, sub_cmd, value, plugin_err_str);

	return (ret);
}

static int
obj_disk_compnum(raid_obj_tab_t *raid_tab, raid_obj_id_t obj_id,
	raid_obj_type_id_t comp_type)
{
	disk_attr_t *attr;
	controller_attr_t *ctl_attrp;
	raid_obj_id_t controller_obj_id;
	raid_lib_t *raid_lib;
	int ret = SUCCESS, fd;

	if (raid_obj_get_type(raid_tab, obj_id) != OBJ_TYPE_DISK)
		return (ERR_DEVICE_TYPE);

	if (comp_type != OBJ_TYPE_DISK_SEG &&
	    comp_type != OBJ_TYPE_HSP &&
	    comp_type != OBJ_TYPE_TASK)
		return (0);
	ret = obj_get_attr(raid_tab, obj_id, (void **)(&attr));
	if ((ret != SUCCESS) || (attr == NULL)) {
		return (ERR_DEVICE_INVALID);
	}
	if (attr->state == DISK_STATE_FAILED) {
		return (SUCCESS);
	}

	controller_obj_id = obj_get_controller(raid_tab, obj_id);
	if (controller_obj_id < OBJ_NONE)
		return (ERR_DEVICE_INVALID);

	ctl_attrp = raid_obj_get_data_ptr(raid_tab, controller_obj_id);
	if (ctl_attrp == NULL) {
		return (ERR_DEVICE_INVALID);
	}

	raid_lib = raid_obj_get_lib(raid_tab, controller_obj_id);
	fd = raid_obj_get_fd(raid_tab, controller_obj_id);
	if ((raid_lib == NULL) || (fd == 0))
		return (ERR_DRIVER_CLOSED);

	ret = raid_lib->compnum(ctl_attrp->controller_id,
	    attr->disk_id, OBJ_TYPE_DISK, comp_type);

	return (ret);
}

static int
obj_disk_complist(raid_obj_tab_t *raid_tab, raid_obj_id_t obj_id,
	int comp_num, raid_obj_id_t *comp_list, raid_obj_type_id_t comp_type)
{
	disk_attr_t *attr;
	controller_attr_t *ctl_attrp;
	raid_obj_id_t controller_obj_id;
	raid_lib_t *raid_lib;
	int ret, i, fd;
	uint32_t *ids;

	if (raid_obj_get_type(raid_tab, obj_id) != OBJ_TYPE_DISK)
		return (ERR_DEVICE_TYPE);

	if (comp_type != OBJ_TYPE_DISK_SEG &&
	    comp_type != OBJ_TYPE_HSP &&
	    comp_type != OBJ_TYPE_TASK)
		return (0);

	if (comp_num <= 0 || comp_list == NULL)
		return (ERR_OP_ILLEGAL);

	for (i = 0; i < comp_num; ++i)
		if (raid_obj_get_type(raid_tab, *(comp_list + i)) !=
		    comp_type)
			return (ERR_DEVICE_TYPE);
	ret = obj_get_attr(raid_tab, obj_id, (void **)(&attr));
	if ((ret != SUCCESS) || (attr == NULL)) {
		return (ERR_DEVICE_INVALID);
	}
	if (attr->state == DISK_STATE_FAILED) {
		return (SUCCESS);
	}

	controller_obj_id = obj_get_controller(raid_tab, obj_id);
	if (controller_obj_id < OBJ_NONE)
		return (ERR_DEVICE_INVALID);

	ctl_attrp = raid_obj_get_data_ptr(raid_tab, controller_obj_id);
	if (ctl_attrp == NULL) {
		return (ERR_DEVICE_INVALID);
	}

	raid_lib = raid_obj_get_lib(raid_tab, controller_obj_id);
	fd = raid_obj_get_fd(raid_tab, controller_obj_id);
	if ((raid_lib == NULL) || (fd == 0))
		return (ERR_DRIVER_CLOSED);

	ids = malloc(comp_num * sizeof (uint32_t));
	if (ids == NULL)
		return (ERR_NOMEM);

	ret = raid_lib->complist(ctl_attrp->controller_id,
	    attr->disk_id, OBJ_TYPE_DISK, comp_type, comp_num, ids);

	if (ret < SUCCESS) {
		free(ids);
		return (ret);
	}

	for (i = 0; i < comp_num; ++ i) {
		diskseg_attr_t *diskseg_attr;
		hsp_attr_t *hsp_attr;
		task_attr_t *task_attr;
		void *attr_buf;

		attr_buf = raid_obj_get_data_ptr(raid_tab, *(comp_list + i));
		if (attr_buf == NULL) {
			free(ids);
			return (ERR_DEVICE_INVALID);
		}

		switch (comp_type) {
		case OBJ_TYPE_DISK_SEG:
			diskseg_attr = attr_buf;
			diskseg_attr->seq_no = *(ids + i);
			break;
		case OBJ_TYPE_HSP:
			hsp_attr = attr_buf;
			hsp_attr->associated_id = *(ids + i);
			break;
		case OBJ_TYPE_TASK:
			task_attr = attr_buf;
			task_attr->task_id = *(ids + i);
			break;
		default:
			free(ids);
			return (ERR_DEVICE_INVALID);
		}
	}


	free(ids);
	return (ret);
}

static int
obj_disk_get_attr(raid_obj_tab_t *raid_tab, raid_obj_id_t obj_id)
{
	disk_attr_t *attr;
	controller_attr_t *ctl_attrp;
	raid_lib_t *raid_lib;
	int ret = SUCCESS, fd;
	raid_obj_id_t controller_obj_id;

	if (raid_obj_get_type(raid_tab, obj_id) != OBJ_TYPE_DISK)
		return (ERR_DEVICE_TYPE);

	if (raid_obj_get_status(raid_tab, obj_id) & OBJ_STATUS_OPENED)
		return (SUCCESS);

	attr = raid_obj_get_data_ptr(raid_tab, obj_id);
	if (attr == NULL)
		return (ERR_DEVICE_INVALID);

	controller_obj_id = obj_get_controller(raid_tab, obj_id);
	if (controller_obj_id < OBJ_NONE)
		return (ERR_DEVICE_INVALID);

	ctl_attrp = raid_obj_get_data_ptr(raid_tab, controller_obj_id);
	if (ctl_attrp == NULL) {
		return (ERR_DEVICE_INVALID);
	}

	raid_lib = raid_obj_get_lib(raid_tab, controller_obj_id);
	fd = raid_obj_get_fd(raid_tab, controller_obj_id);
	if ((raid_lib == NULL) || (fd == 0))
		return (ERR_DRIVER_CLOSED);

	ret = raid_lib->get_attr(ctl_attrp->controller_id,
	    attr->disk_id, 0, OBJ_TYPE_DISK, attr);

	if (ret < SUCCESS)
		return (ret);

	(void) raid_obj_set_status(raid_tab, obj_id, OBJ_STATUS_OPENED);

	return (ret);
}

static int
obj_hsp_get_attr(raid_obj_tab_t *raid_tab, raid_obj_id_t obj_id)
{
	hsp_attr_t *attr;

	if (raid_obj_get_type(raid_tab, obj_id) != OBJ_TYPE_HSP)
		return (ERR_DEVICE_TYPE);

	if (raid_obj_get_status(raid_tab, obj_id) & OBJ_STATUS_OPENED)
		return (SUCCESS);

	attr = raid_obj_get_data_ptr(raid_tab, obj_id);
	if (attr == NULL)
		return (ERR_DEVICE_INVALID);

	if (attr->associated_id == (uint32_t)OBJ_ATTR_NONE)
		attr->type = HSP_TYPE_GLOBAL;
	else
		attr->type = HSP_TYPE_LOCAL;

	return (SUCCESS);
}

static int
obj_arraypart_get_attr(raid_obj_tab_t *raid_tab, raid_obj_id_t obj_id)
{
	arraypart_attr_t *attr;
	array_attr_t *array_attr;
	controller_attr_t *ctl_attrp;
	raid_lib_t *raid_lib;
	int ret = SUCCESS, fd;
	raid_obj_id_t controller_obj_id, array_obj_id;

	if (raid_obj_get_type(raid_tab, obj_id) != OBJ_TYPE_ARRAY_PART)
		return (ERR_DEVICE_TYPE);

	if (raid_obj_get_status(raid_tab, obj_id) & OBJ_STATUS_OPENED)
		return (SUCCESS);

	attr = raid_obj_get_data_ptr(raid_tab, obj_id);
	if (attr == NULL)
		return (ERR_DEVICE_INVALID);

	array_obj_id = raid_obj_get_container(raid_tab, obj_id);
	if (array_obj_id < OBJ_NONE)
		return (ERR_DEVICE_INVALID);

	array_attr = raid_obj_get_data_ptr(raid_tab, array_obj_id);
	if (array_attr == NULL)
		return (ERR_DEVICE_INVALID);

	controller_obj_id = obj_get_controller(raid_tab, obj_id);
	if (controller_obj_id < OBJ_NONE)
		return (ERR_DEVICE_INVALID);

	ctl_attrp = raid_obj_get_data_ptr(raid_tab, controller_obj_id);
	if (ctl_attrp == NULL) {
		return (ERR_DEVICE_INVALID);
	}

	raid_lib = raid_obj_get_lib(raid_tab, controller_obj_id);
	fd = raid_obj_get_fd(raid_tab, controller_obj_id);
	if ((raid_lib == NULL) || (fd == 0))
		return (ERR_DRIVER_CLOSED);

	ret = raid_lib->get_attr(ctl_attrp->controller_id,
	    array_attr->array_id, attr->disk_id,
	    OBJ_TYPE_ARRAY_PART, attr);

	if (ret < SUCCESS)
		return (ret);

	(void) raid_obj_set_status(raid_tab, obj_id, OBJ_STATUS_OPENED);

	return (ret);
}

static int
obj_diskseg_get_attr(raid_obj_tab_t *raid_tab, raid_obj_id_t obj_id)
{
	diskseg_attr_t *attr;
	disk_attr_t *disk_attr;
	controller_attr_t *ctl_attrp;
	raid_lib_t *raid_lib;
	int ret = SUCCESS, fd;
	raid_obj_id_t controller_obj_id, disk_obj_id;

	if (raid_obj_get_type(raid_tab, obj_id) != OBJ_TYPE_DISK_SEG)
		return (ERR_DEVICE_TYPE);

	if (raid_obj_get_status(raid_tab, obj_id) & OBJ_STATUS_OPENED)
		return (SUCCESS);

	attr = raid_obj_get_data_ptr(raid_tab, obj_id);
	if (attr == NULL)
		return (ERR_DEVICE_INVALID);

	disk_obj_id = raid_obj_get_container(raid_tab, obj_id);
	if (disk_obj_id < OBJ_NONE)
		return (ERR_DEVICE_INVALID);

	disk_attr = raid_obj_get_data_ptr(raid_tab, disk_obj_id);
	if (disk_attr == NULL)
		return (ERR_DEVICE_INVALID);

	controller_obj_id = obj_get_controller(raid_tab, obj_id);
	if (controller_obj_id < OBJ_NONE)
		return (ERR_DEVICE_INVALID);

	ctl_attrp = raid_obj_get_data_ptr(raid_tab, controller_obj_id);
	if (ctl_attrp == NULL) {
		return (ERR_DEVICE_INVALID);
	}

	raid_lib = raid_obj_get_lib(raid_tab, controller_obj_id);
	fd = raid_obj_get_fd(raid_tab, controller_obj_id);
	if ((raid_lib == NULL) || (fd == 0))
		return (ERR_DRIVER_CLOSED);

	ret = raid_lib->get_attr(ctl_attrp->controller_id,
	    disk_attr->disk_id, attr->seq_no, OBJ_TYPE_DISK_SEG, attr);

	if (ret < SUCCESS)
		return (ret);

	(void) raid_obj_set_status(raid_tab, obj_id, OBJ_STATUS_OPENED);

	return (ret);
}

static int
obj_task_get_attr(raid_obj_tab_t *raid_tab, raid_obj_id_t obj_id)
{
	task_attr_t *attr;
	controller_attr_t *ctl_attrp;
	raid_lib_t *raid_lib;
	int ret = SUCCESS, fd;
	raid_obj_id_t controller_obj_id;

	if (raid_obj_get_type(raid_tab, obj_id) != OBJ_TYPE_TASK)
		return (ERR_DEVICE_TYPE);

	attr = raid_obj_get_data_ptr(raid_tab, obj_id);
	if (attr == NULL)
		return (ERR_DEVICE_INVALID);

	controller_obj_id = obj_get_controller(raid_tab, obj_id);
	if (controller_obj_id < OBJ_NONE)
		return (ERR_DEVICE_INVALID);

	ctl_attrp = raid_obj_get_data_ptr(raid_tab, controller_obj_id);
	if (ctl_attrp == NULL) {
		return (ERR_DEVICE_INVALID);
	}

	raid_lib = raid_obj_get_lib(raid_tab, controller_obj_id);
	fd = raid_obj_get_fd(raid_tab, controller_obj_id);
	if ((raid_lib == NULL) || (fd == 0))
		return (ERR_DRIVER_CLOSED);

	ret = raid_lib->get_attr(ctl_attrp->controller_id,
	    attr->task_id, OBJ_ATTR_NONE, OBJ_TYPE_TASK, attr);

	return (ret);
}

static int
obj_array_create(raid_obj_tab_t *raid_tab, raid_obj_id_t array_obj_id,
	int num_of_comp, raid_obj_id_t *disk_list, char **plugin_err_str)
{
	controller_attr_t *controller_attr;
	array_attr_t *array_attr, array_attr2;
	disk_attr_t *disk_attr;
	arraypart_attr_t *arraypart_attrs;
	disk_tag_t *disk_tags;
	raid_obj_id_t obj_id, controller_obj_id = OBJ_NONE;
	raid_lib_t *raid_lib;
	int i, j, ret, fd;
	int disk_cnt = 0, disk_set_num = 0, set_num = 0, layer_cnt = 0;
	uint64_t min_disk_capacity = 0;
	uint32_t *diskid_list;

	array_attr = raid_obj_get_data_ptr(raid_tab, array_obj_id);
	if (array_attr == NULL)
		return (ERR_DEVICE_INVALID);

	/* Check the disk layout expression */
	if (disk_list[0] != OBJ_SEPARATOR_BEGIN ||
	    disk_list[num_of_comp - 1] != OBJ_SEPARATOR_END)
		return (ERR_ARRAY_LAYOUT);
	for (i = 0; i < num_of_comp; ++i) {
		if (disk_list[i] == OBJ_SEPARATOR_BEGIN) {
			if (disk_cnt != 0)
				return (ERR_ARRAY_LAYOUT);
			++layer_cnt;
			continue;
		}
		if (disk_list[i] == OBJ_SEPARATOR_END) {
			if (disk_set_num == 0)
				disk_set_num = disk_cnt;
			else if (disk_set_num != disk_cnt && disk_cnt != 0)
				return (ERR_ARRAY_LAYOUT);
			disk_cnt = 0;
			++set_num;
			--layer_cnt;
			continue;
		}
		switch (array_attr->raid_level) {
		case RAID_LEVEL_0:
		case RAID_LEVEL_1:
		case RAID_LEVEL_1E:
		case RAID_LEVEL_5:
			if (layer_cnt != 1)
				return (ERR_ARRAY_LAYOUT);
			break;
		case RAID_LEVEL_10:
		case RAID_LEVEL_50:
			if (layer_cnt != 2)
				return (ERR_ARRAY_LAYOUT);
			break;
		default:
			return (ERR_ARRAY_LEVEL);
		}
		++disk_cnt;
	}

	if (layer_cnt != 0)
		return (ERR_ARRAY_LAYOUT);

	switch (array_attr->raid_level) {
	case RAID_LEVEL_0:
		if (disk_set_num < 2 || set_num != 1)
			return (ERR_ARRAY_LAYOUT);
		break;
	case RAID_LEVEL_1:
		if (disk_set_num != 2 || set_num != 1)
			return (ERR_ARRAY_LAYOUT);
		break;
	case RAID_LEVEL_1E:
	case RAID_LEVEL_5:
		if (disk_set_num < 3 || set_num != 1)
			return (ERR_ARRAY_LAYOUT);
		break;
	case RAID_LEVEL_10:
		if (disk_set_num != 2 || set_num < 2)
			return (ERR_ARRAY_LAYOUT);
		break;
	case RAID_LEVEL_50:
		if (disk_set_num < 3 || set_num < 2)
			return (ERR_ARRAY_LAYOUT);
		break;
	default:
		return (ERR_ARRAY_LEVEL);
	}

	arraypart_attrs = calloc(num_of_comp, sizeof (arraypart_attr_t));
	if (arraypart_attrs == NULL)
		return (ERR_NOMEM);

	for (i = 0; i < num_of_comp; ++i) {
		/* Keep seperators */
		if (*(disk_list + i) == OBJ_SEPARATOR_BEGIN) {
			arraypart_attrs[i].disk_id =
			    (uint32_t)OBJ_SEPARATOR_BEGIN;
			continue;
		}

		if (*(disk_list + i) == OBJ_SEPARATOR_END) {
			arraypart_attrs[i].disk_id =
			    (uint32_t)OBJ_SEPARATOR_END;
			continue;
		}

		disk_cnt++;
		/* Check if it's a disk */
		if (raid_obj_get_type(raid_tab, *(disk_list + i)) !=
		    OBJ_TYPE_DISK)
			return (ERR_DEVICE_TYPE);

		/* Check if it's duplicated with other disks */
		for (j = 0; j < i; ++j)
			if (*(disk_list + j) == *(disk_list + i)) {
				free(arraypart_attrs);
				return (ERR_DEVICE_DUP);
			}

		/* Check disk status */
		ret = obj_get_attr(raid_tab, *(disk_list + i),
		    (void **)(&disk_attr));
		if (ret != SUCCESS)
			return (ret);

		if (disk_attr->state != DISK_STATE_GOOD) {
			free(arraypart_attrs);
			return (ERR_DISK_STATE);
		}

		/* All disks must belong to the same controller */
		obj_id = obj_get_controller(raid_tab, *(disk_list + i));
		if (obj_id <= OBJ_NONE)
			return (obj_id);
		if (controller_obj_id == OBJ_NONE) {
			controller_obj_id = obj_id;
			ret = obj_get_attr(raid_tab, controller_obj_id,
			    (void **)(&controller_attr));
		} else if (obj_id != controller_obj_id) {
			free(arraypart_attrs);
			return (ERR_DRIVER_ACROSS);
		}

		/* Check if the disk contains too many segments */
		obj_id = obj_get_comp(raid_tab, *(disk_list + i),
		    OBJ_TYPE_DISK_SEG);
		j = 0;
		while (obj_id > OBJ_NONE) {
			++j;
			obj_id = obj_get_sibling(raid_tab, obj_id);
		}
		if (j > controller_attr->max_seg_per_disk) {
			free(arraypart_attrs);
			return (ERR_DISK_SEG_AMOUNT);
		}

		/* Check if controller is a hostraid controller */
		if (controller_attr->capability & RAID_CAP_DISK_TRANS) {
			/*
			 * For hostraid, the first disk should
			 * be with of minimum capacity
			 */
			if (min_disk_capacity == 0) {
				min_disk_capacity = disk_attr->capacity;

				/* Can not specify capacity for hostraid */
				if (array_attr->capacity != 0) {
					free(arraypart_attrs);
					return (ERR_OP_ILLEGAL);
				}
			} else if (min_disk_capacity > disk_attr->capacity) {
				free(arraypart_attrs);
				return (ERR_DISK_SPACE);
			}

			/* Disk should not be used for hostraid */
			obj_id = obj_get_comp(raid_tab, *(disk_list + i),
			    OBJ_TYPE_DISK_SEG);
			if (obj_id < OBJ_NONE) {
				free(arraypart_attrs);
				return (obj_id);
			} else if (obj_id > OBJ_NONE) {
				free(arraypart_attrs);
				return (ERR_DISK_NOT_EMPTY);
			}
		}

		arraypart_attrs[i].disk_id = disk_attr->disk_id;
		arraypart_attrs[i].offset = OBJ_ATTR_NONE;
		arraypart_attrs[i].size = OBJ_ATTR_NONE;
	}

	/* Check if array amount exceeds limit */
	if (controller_attr->max_array_num <=
	    obj_controller_compnum(raid_tab, controller_obj_id,
	    OBJ_TYPE_ARRAY))
		return (ERR_ARRAY_AMOUNT);


	raid_lib = raid_obj_get_lib(raid_tab, controller_obj_id);
	fd = raid_obj_get_fd(raid_tab, controller_obj_id);
	if ((raid_lib == NULL) || (fd == 0))
		return (ERR_DRIVER_CLOSED);

	/* Check if the controller can support the array RAID level */
	switch (array_attr->raid_level) {
	case	RAID_LEVEL_0:
		if (!(controller_attr->capability & RAID_CAP_RAID0)) {
			free(arraypart_attrs);
			return (ERR_ARRAY_LEVEL);
		}
		break;
	case	RAID_LEVEL_1:
		if (!(controller_attr->capability & RAID_CAP_RAID1)) {
			free(arraypart_attrs);
			return (ERR_ARRAY_LEVEL);
		}
		break;
	case	RAID_LEVEL_1E:
		if (!(controller_attr->capability & RAID_CAP_RAID1E)) {
			free(arraypart_attrs);
			return (ERR_ARRAY_LEVEL);
		}
		break;
	case	RAID_LEVEL_5:
		if (!(controller_attr->capability & RAID_CAP_RAID5)) {
			free(arraypart_attrs);
			return (ERR_ARRAY_LEVEL);
		}
		break;
	case	RAID_LEVEL_10:
		if (!(controller_attr->capability & RAID_CAP_RAID10)) {
			free(arraypart_attrs);
			return (ERR_ARRAY_LEVEL);
		}
		break;
	case	RAID_LEVEL_50:
		if (!(controller_attr->capability & RAID_CAP_RAID50)) {
			free(arraypart_attrs);
			return (ERR_ARRAY_LEVEL);
		}
		break;
	default:
		free(arraypart_attrs);
		return (ERR_ARRAY_LEVEL);
	}

	/* Check if plug in can calculate the maximum size */
	(void) memcpy(&array_attr2, array_attr, sizeof (array_attr_t));
	array_attr2.capacity = OBJ_ATTR_NONE;
	ret = raid_lib->array_create(controller_attr->controller_id,
	    &array_attr2, num_of_comp, arraypart_attrs, plugin_err_str);

	/* If plugin/driver will not calculate space */
	if (ret == ERR_OP_NO_IMPL) {
		/* Calculate the maximum capacity */
		array_attr2.capacity = raid_space_noalign(raid_tab,
		    array_attr2.raid_level, num_of_comp, disk_list,
		    arraypart_attrs);

		/*
		 * If controller is capable to allocate space,
		 * set offset and size attributes to OBJ_ATTR_NONE
		 * and let the controller to determine these value
		 */
		if (controller_attr->capability & RAID_CAP_SMART_ALLOC)
			for (i = 0; i < num_of_comp; ++i) {
				arraypart_attrs[i].offset =
				    OBJ_ATTR_NONE;
				arraypart_attrs[i].size =
				    OBJ_ATTR_NONE;
			}

		/* There's no enough space for specified capacity */
		if (array_attr->capacity > array_attr2.capacity) {
			free(arraypart_attrs);
			return (ERR_ARRAY_SIZE);
		}

		/* capacity == 0, allocate maximum space */
		if (array_attr->capacity == 0)
			array_attr->capacity = array_attr2.capacity;
	} else if (ret < SUCCESS) {
		free(arraypart_attrs);
		return (ret);
	} else if (array_attr2.capacity < array_attr->capacity) {
		/* Return the maximum size */
		array_attr->capacity = array_attr2.capacity;
		free(arraypart_attrs);
		return (ERR_ARRAY_SIZE);
	}

	if (array_attr->capacity < ARRAYPART_MIN_SIZE * disk_cnt) {
		free(arraypart_attrs);
		return (ERR_ARRAY_SIZE);
	}


	ret = raid_lib->array_create(controller_attr->controller_id,
	    array_attr, num_of_comp, arraypart_attrs, plugin_err_str);
	free(arraypart_attrs);

	if (ret != SUCCESS)
		return (ret);

	/* Add array object into device tree so that we can map the handle */
	(void) raid_obj_add_org(raid_tab, array_obj_id, controller_obj_id);

	/* unconfig disk minor nodes if it's hostraid */
	if (controller_attr->capability & RAID_CAP_DISK_TRANS) {
		diskid_list = (uint32_t *)calloc(num_of_comp,
		    sizeof (uint32_t));
		if (diskid_list == NULL) {
			return (ERR_NOMEM);
		}
		disk_tags = (disk_tag_t *)calloc(num_of_comp,
		    sizeof (disk_tag_t));
		if (disk_tags == NULL) {
			return (ERR_NOMEM);
		}

		for (i = 0; i < num_of_comp; ++i) {
			if (*(disk_list + i) == OBJ_SEPARATOR_BEGIN) {
				diskid_list[i] = (uint32_t)OBJ_SEPARATOR_BEGIN;
			} else if (*(disk_list + i) == OBJ_SEPARATOR_END) {
				diskid_list[i] = (uint32_t)OBJ_SEPARATOR_END;
			} else {
				ret = obj_get_attr(raid_tab, *(disk_list + i),
				    (void **)(&disk_attr));
				if (ret != SUCCESS) {
					free(diskid_list);
					return (ret);
				}
				diskid_list[i] = disk_attr->disk_id;
				disk_tags[i] = disk_attr->tag;
			}
		}

		for (i = 0; i < num_of_comp; ++i) {
			if (diskid_list[i] == (uint32_t)OBJ_SEPARATOR_BEGIN ||
			    diskid_list[i] == (uint32_t)OBJ_SEPARATOR_END) {
				continue;
			}

			if ((disk_tags[i].cidl.target_id ==
			    array_attr->tag.idl.target_id) &&
			    (disk_tags[i].cidl.lun ==
			    array_attr->tag.idl.lun))
				continue;

			ret = raid_dev_config(CFGA_CMD_UNCONFIGURE,
			    controller_attr->controller_id, diskid_list[i], 0);
			if (ret != SUCCESS) {
				free(diskid_list);
				free(disk_tags);
				return (ret);
			}
		}
		free(diskid_list);
		free(disk_tags);
	} else {
		/* for HW raid */
		ret = raid_dev_config(CFGA_CMD_CONFIGURE,
		    controller_attr->controller_id, array_attr->array_id, 1);
	}

	return (ret);
}

static int
obj_array_delete(raid_obj_tab_t *raid_tab, raid_obj_id_t array_obj_id,
	char **plugin_err_str)
{
	raid_obj_id_t controller_obj_id;
	controller_attr_t *controller_attr;
	array_attr_t *array_attr;
	arraypart_attr_t *arraypart_attr;
	disk_attr_t *disk_attr;
	raid_obj_id_t arraypart_obj_id;
	raid_lib_t *raid_lib;
	int i = 0, j = 0, ret, fd;
	uint32_t *disk_ids = NULL;

	controller_obj_id = obj_get_controller(raid_tab, array_obj_id);
	if (controller_obj_id <= OBJ_NONE)
		return (controller_obj_id);

	ret = obj_get_attr(raid_tab, controller_obj_id,
	    (void **)(&controller_attr));
	if (ret < SUCCESS) {
		return (ret);
	}
	ret = obj_get_attr(raid_tab, array_obj_id, (void **)(&array_attr));
	if (ret < SUCCESS)
		return (ret);

	raid_lib = raid_obj_get_lib(raid_tab, controller_obj_id);
	fd = raid_obj_get_fd(raid_tab, controller_obj_id);
	if ((raid_lib == NULL) || (fd == 0))
		return (ERR_DRIVER_CLOSED);

	/* change minor nodes state for disks */
	if (controller_attr->capability & RAID_CAP_DISK_TRANS) {
		arraypart_obj_id = obj_get_comp(raid_tab, array_obj_id,
		    OBJ_TYPE_ARRAY_PART);
		if (arraypart_obj_id < OBJ_NONE) {
			return (arraypart_obj_id);
		}

		/*
		 * Check how many disks in volume and malloc space for
		 * disk_ids; note that the number should be the disk
		 * number minors 1 since the primary disk should not
		 * be counted in.
		 */
		while (arraypart_obj_id = obj_get_sibling(raid_tab,
		    arraypart_obj_id)) {
			if (arraypart_obj_id < OBJ_NONE)
				return (arraypart_obj_id);
			++i;
		}
		disk_ids = calloc(i, sizeof (uint32_t));
		if (disk_ids == NULL)
			return (ERR_NOMEM);

		/* Stor all member disk ids into disk_ids */
		arraypart_obj_id = obj_get_comp(raid_tab, array_obj_id,
		    OBJ_TYPE_ARRAY_PART);

		while (arraypart_obj_id > OBJ_NONE) {
			ret = obj_get_attr(raid_tab, arraypart_obj_id,
			    (void **)(&arraypart_attr));
			if (ret != SUCCESS) {
				return (ret);
			}

			ret = obj_get_attr(raid_tab, arraypart_obj_id,
			    (void **)(&disk_attr));
			if (ret != SUCCESS) {
				return (ret);
			}

			if (array_attr->tag.idl.target_id ==
			    disk_attr->tag.cidl.target_id &&
			    array_attr->tag.idl.lun ==
			    disk_attr->tag.cidl.target_id) {
				arraypart_obj_id = obj_get_sibling(raid_tab,
				    arraypart_obj_id);
				continue;
			}

			disk_ids[j] = arraypart_attr->disk_id;
			++j;
			arraypart_obj_id = obj_get_sibling(raid_tab,
			    arraypart_obj_id);
		}
	} else {
		ret = raid_dev_config(CFGA_CMD_UNCONFIGURE,
		    controller_attr->controller_id, array_attr->array_id, 1);
		if (ret != SUCCESS)
			return (ret);
	}

	ret = raid_lib->array_delete(controller_attr->controller_id,
	    array_attr->array_id, plugin_err_str);
	if (ret < SUCCESS) {
		if (disk_ids)
			free(disk_ids);
		return (ret);
	}

	if (controller_attr->capability & RAID_CAP_DISK_TRANS) {
		for (i = 0; i < j; ++i)
			ret = raid_dev_config(CFGA_CMD_CONFIGURE,
			    controller_attr->controller_id,
			    disk_ids[i], 0);
			if (ret == ERR_ARRAY_CONFIG)
				ret = SUCCESS;
			else if (ret < SUCCESS) {
				free(disk_ids);
				return (ret);
			}
	}

	if (disk_ids)
		free(disk_ids);
	return (ret);
}

static int
obj_hsp_bind(raid_obj_tab_t *raid_tab, int num, raid_obj_id_t *obj_ids,
	char **plugin_err_str)
{
	raid_obj_id_t obj_id, controller_obj_id = OBJ_NONE;
	raid_obj_id_t array_obj_id, disk_obj_id;
	hsp_relation_t *hsp_relation;
	controller_attr_t *controller_attr;
	array_attr_t *array_attr;
	arraypart_attr_t *arraypart_attr;
	disk_attr_t *disk_attr;
	diskseg_attr_t *diskseg_attr;
	hsp_attr_t *hsp_attr;
	raid_lib_t *raid_lib;
	int ret, fd, i, j = 0;

	hsp_relation = malloc(sizeof (hsp_relation_t) * num);
	if (hsp_relation == NULL)
		return (ERR_NOMEM);

	for (i = 0; i < num; ++i) {
		array_obj_id = *(obj_ids + i * 2);
		disk_obj_id = *(obj_ids + i * 2 + 1);

		if (raid_obj_get_type(raid_tab, disk_obj_id) != OBJ_TYPE_DISK ||
		    (array_obj_id != OBJ_ATTR_NONE &&
		    raid_obj_get_type(raid_tab, array_obj_id) !=
		    OBJ_TYPE_ARRAY)) {
			free(hsp_relation);
			return (ERR_DEVICE_TYPE);
		}

		/* Get controller attributes */
		if (controller_obj_id == OBJ_NONE)
			controller_obj_id = obj_get_controller(raid_tab,
			    disk_obj_id);
		else if (controller_obj_id != obj_get_controller(raid_tab,
		    disk_obj_id)) {
			free(hsp_relation);
			return (ERR_DRIVER_ACROSS);
		}

		ret = obj_get_attr(raid_tab, controller_obj_id,
		    (void **)(&controller_attr));

		/* Get disk attributes */
		ret = obj_get_attr(raid_tab,  disk_obj_id,
		    (void **)(&disk_attr));
		if (disk_attr->state == DISK_STATE_FAILED) {
			free(hsp_relation);
			return (ERR_DISK_STATE);
		}

		/* If it's not a hsp disk, check if there's occupied space */
		if (obj_get_comp(raid_tab, disk_obj_id, OBJ_TYPE_HSP) ==
		    OBJ_NONE) {
			obj_id = obj_get_comp(raid_tab, disk_obj_id,
			    OBJ_TYPE_DISK_SEG);
			while (obj_id != OBJ_NONE) {
				ret = obj_get_attr(raid_tab, obj_id,
				    (void **)(&diskseg_attr));
				if (!(diskseg_attr->state &
				    DISKSEG_STATE_RESERVED)) {
					free(hsp_relation);
					return (ERR_DISK_NOT_EMPTY);
				}
				obj_id = obj_get_sibling(raid_tab, obj_id);
			}
		}

		if (array_obj_id != OBJ_ATTR_NONE) {
			/* If local hsp is supported */
			if (!(controller_attr->capability & RAID_CAP_L_HSP)) {
				free(hsp_relation);
				return (ERR_OP_ILLEGAL);
			}

			if (raid_obj_get_type(raid_tab, array_obj_id) !=
			    OBJ_TYPE_ARRAY) {
				free(hsp_relation);
				return (ERR_DEVICE_TYPE);
			}

			/* Get array attributes */
			ret = obj_get_attr(raid_tab, array_obj_id,
			    (void **)(&array_attr));
			/* RAID 0 array can not use hsp */
			if (array_attr->raid_level == RAID_LEVEL_0) {
				free(hsp_relation);
				return (ERR_ARRAY_LEVEL);
			}

			/* If It's belong to another controller */
			if (controller_obj_id != obj_get_controller(raid_tab,
			    array_obj_id)) {
				free(hsp_relation);
				return (ERR_DRIVER_ACROSS);
			}

			/* Get an array part attributes */
			if ((array_attr->raid_level == RAID_LEVEL_10) ||
			    (array_attr->raid_level == RAID_LEVEL_50))
				obj_id = obj_get_comp(raid_tab, array_obj_id,
				    OBJ_TYPE_ARRAY);
			else
				obj_id = array_obj_id;
			obj_id = obj_get_comp(raid_tab, obj_id,
			    OBJ_TYPE_ARRAY_PART);
			ret = obj_get_attr(raid_tab, obj_id,
			    (void **)(&arraypart_attr));

			/* Check if disk space is enough for array */
			if (arraypart_attr->size > disk_attr->capacity) {
				free(hsp_relation);
				return (ERR_DISK_SPACE);
			}
			if (controller_attr->capability & RAID_CAP_ARRAY_ALIGN)
				if ((arraypart_attr->size +
				    arraypart_attr->offset) >
				    disk_attr->capacity) {
				free(hsp_relation);
				return (ERR_DISK_SPACE);
				}
		} else if (!(controller_attr->capability & RAID_CAP_G_HSP)) {
			/* if global hsp is supported */
			free(hsp_relation);
			return (ERR_OP_ILLEGAL);
		}

		/*
		 * If the array is already associated with the
		 * local hsp, or it's a global hsp, ignore it
		 */
		obj_id = obj_get_comp(raid_tab, disk_obj_id, OBJ_TYPE_HSP);
		if (obj_id > OBJ_NONE) {
			if (obj_get_attr(raid_tab, obj_id,
			    (void **)&hsp_attr) >= SUCCESS) {
				if (((hsp_attr->type == HSP_TYPE_GLOBAL) &&
				    (array_obj_id != OBJ_ATTR_NONE)) ||
				    ((hsp_attr->type == HSP_TYPE_LOCAL) &&
				    (array_obj_id == OBJ_ATTR_NONE))) {
					free(hsp_relation);
					return (ERR_OP_ILLEGAL);
				}
			}
		}

		if (array_obj_id != OBJ_ATTR_NONE)
			hsp_relation[j].array_id = array_attr->array_id;
		else
			hsp_relation[j].array_id = (uint32_t)OBJ_ATTR_NONE;
		hsp_relation[j].disk_id = disk_attr->disk_id;
		++ j;
	}


	if (j == 0)
		return (SUCCESS);

	raid_lib = raid_obj_get_lib(raid_tab, controller_obj_id);
	fd = raid_obj_get_fd(raid_tab, controller_obj_id);
	if ((raid_lib == NULL) || (fd == 0))
		return (ERR_DRIVER_CLOSED);

	if (raid_lib->hsp_bind == NULL) {
		free(hsp_relation);
		return (ERR_OP_NO_IMPL);
	}

	ret = raid_lib->hsp_bind(controller_attr->controller_id,
	    j, hsp_relation, plugin_err_str);

	free(hsp_relation);
	return (ret);
}

static int
obj_hsp_unbind(raid_obj_tab_t *raid_tab, int num, raid_obj_id_t *obj_ids,
	char **plugin_err_str)
{
	raid_obj_id_t obj_id, controller_obj_id = OBJ_NONE;
	raid_obj_id_t array_obj_id, disk_obj_id;
	hsp_relation_t *hsp_relation;
	controller_attr_t *controller_attr;
	array_attr_t *array_attr;
	disk_attr_t *disk_attr;
	hsp_attr_t *hsp_attr;
	raid_lib_t *raid_lib;
	int ret, fd, i, j = 0;

	hsp_relation = malloc(sizeof (hsp_relation_t) * num);
	if (hsp_relation == NULL)
		return (ERR_NOMEM);

	for (i = 0; i < num; ++i) {
		array_obj_id = *(obj_ids + i * 2);
		disk_obj_id = *(obj_ids + i * 2 + 1);

		if (raid_obj_get_type(raid_tab, disk_obj_id) != OBJ_TYPE_DISK) {
			free(hsp_relation);
			return (ERR_DEVICE_TYPE);
		}

		/* Get controller attributes */
		if (controller_obj_id == OBJ_NONE)
			controller_obj_id = obj_get_controller(raid_tab,
			    disk_obj_id);
		else if (controller_obj_id != obj_get_controller(raid_tab,
		    disk_obj_id)) {
			free(hsp_relation);
			return (ERR_DRIVER_ACROSS);
		}

		ret = obj_get_attr(raid_tab, controller_obj_id,
		    (void **)(&controller_attr));

		/* Get disk attributes */
		ret = obj_get_attr(raid_tab,  disk_obj_id,
		    (void **)(&disk_attr));
		if (disk_attr->state == DISK_STATE_FAILED) {
			free(hsp_relation);
			return (ERR_DISK_STATE);
		}

		/* If it's not a hsp disk */
		obj_id = obj_get_comp(raid_tab, disk_obj_id, OBJ_TYPE_HSP);
		if (obj_id == OBJ_NONE) {
			free(hsp_relation);
			return (ERR_DISK_STATE);
		}
		ret = obj_get_attr(raid_tab, obj_id, (void **)(&hsp_attr));

		if (array_obj_id != OBJ_ATTR_NONE) {
			if (raid_obj_get_type(raid_tab, array_obj_id) !=
			    OBJ_TYPE_ARRAY) {
				free(hsp_relation);
				return (ERR_DEVICE_TYPE);
			}

			/* Get array attributes */
			ret = obj_get_attr(raid_tab, array_obj_id,
			    (void **)(&array_attr));

			/* If It's belong to another controller */
			if (controller_obj_id != obj_get_controller(raid_tab,
			    array_obj_id)) {
				free(hsp_relation);
				return (ERR_DRIVER_ACROSS);
			}

			/* If want to remove an array from a global hsp */
			if (hsp_attr->type == HSP_TYPE_GLOBAL) {
				free(hsp_relation);
				return (ERR_OP_ILLEGAL);
			}

			do {
				(void) obj_get_attr(raid_tab, obj_id,
				    (void **)(&hsp_attr));

				if (hsp_attr->associated_id ==
				    array_attr->array_id ||
				    hsp_attr->type == HSP_TYPE_GLOBAL)
					break;

				obj_id = obj_get_sibling(raid_tab, obj_id);
			} while (obj_id > OBJ_NONE);
		} else if (hsp_attr->type != HSP_TYPE_GLOBAL) {
			/* if global hsp is supported */
			free(hsp_relation);
			return (ERR_OP_ILLEGAL);
		}

		/*
		 * If array is associated with a local hsp, or remove a
		 * global hsp disk
		 */
		if ((obj_id && (array_obj_id != OBJ_ATTR_NONE)) ||
		    (array_obj_id == OBJ_ATTR_NONE)) {
			if (array_obj_id != OBJ_ATTR_NONE)
				hsp_relation[j].array_id = array_attr->array_id;
			else
				hsp_relation[j].array_id =
				    (uint32_t)OBJ_ATTR_NONE;
			hsp_relation[j].disk_id = disk_attr->disk_id;
			++ j;
		} else {
			free(hsp_relation);
			return (ERR_OP_ILLEGAL);
		}
	}


	raid_lib = raid_obj_get_lib(raid_tab, controller_obj_id);
	fd = raid_obj_get_fd(raid_tab, controller_obj_id);
	if ((raid_lib == NULL) || (fd == 0))
		return (ERR_DRIVER_CLOSED);

	if (raid_lib->hsp_unbind == NULL) {
		free(hsp_relation);
		return (ERR_OP_NO_IMPL);
	}

	ret = raid_lib->hsp_unbind(controller_attr->controller_id,
	    j, hsp_relation, plugin_err_str);

	free(hsp_relation);
	return (ret);
}

/*
 * Object maintennance routines
 */
static int
raid_obj_create_system_obj(raid_obj_tab_t *raid_tab)
{
	raid_obj_t *raid_obj;
	int ret;

	raid_obj = calloc(1, sizeof (raid_obj_t));
	if (raid_obj == NULL)
		return (ERR_NOMEM);

	raid_obj->obj_id = OBJ_SYSTEM;
	raid_obj->obj_type_id = OBJ_TYPE_SYSTEM;
	raid_obj->data = NULL;

	ret = raid_obj_tab_insert(raid_tab, raid_obj->obj_id, raid_obj);
	if (ret == ERR_DEVICE_DUP) {
		free(raid_obj);
		return (ERR_DEVICE_UNCLEAN);
	}

	return (SUCCESS);
}

static raid_obj_id_t
raid_obj_id_new(raid_obj_tab_t *raid_tab)
{
	++ raid_tab->obj_id_cnt;
	if (raid_tab->obj_id_cnt <= 0)
		return (ERR_DEVICE_OVERFLOW);

	return (raid_tab->obj_id_cnt);
}

static void *
raid_obj_attr_new(raid_obj_type_id_t obj_type)
{
	void *obj_attr = NULL;

	switch (obj_type) {
	case	OBJ_TYPE_CONTROLLER:
		obj_attr = calloc(1, sizeof (controller_attr_t));
		break;
	case	OBJ_TYPE_ARRAY:
		obj_attr = calloc(1, sizeof (array_attr_t));
		break;
	case	OBJ_TYPE_DISK:
		obj_attr = calloc(1, sizeof (disk_attr_t));
		break;
	case	OBJ_TYPE_HSP:
		obj_attr = calloc(1, sizeof (hsp_attr_t));
		break;
	case	OBJ_TYPE_ARRAY_PART:
		obj_attr = calloc(1, sizeof (arraypart_attr_t));
		break;
	case	OBJ_TYPE_DISK_SEG:
		obj_attr = calloc(1, sizeof (diskseg_attr_t));
		break;
	case	OBJ_TYPE_TASK:
		obj_attr = calloc(1, sizeof (task_attr_t));
		break;
	default:
		break;
	}

	return (obj_attr);
}

static raid_obj_id_t
raid_obj_create(raid_obj_tab_t *raid_tab, raid_obj_type_id_t obj_type)
{
	raid_obj_t *raid_obj;
	int ret;
	void *data_ptr;

	raid_obj = calloc(1, sizeof (raid_obj_t));
	if (raid_obj == NULL)
		return (ERR_NOMEM);

	raid_obj->obj_id = raid_obj_id_new(raid_tab);
	if (raid_obj->obj_id < OBJ_NONE)
		return (ERR_DEVICE_OVERFLOW);

	ret = raid_obj_tab_insert(raid_tab, raid_obj->obj_id, raid_obj);
	if (ret == ERR_DEVICE_DUP) {
		free(raid_obj);
		return (ERR_DEVICE_DUP);
	}

	data_ptr = raid_obj_attr_new(obj_type);
	if (data_ptr == NULL) {
		(void) raid_obj_delete(raid_tab, raid_obj->obj_id);
		return (ERR_NOMEM);
	}

	(void) raid_obj_set_data_ptr(raid_tab, raid_obj->obj_id, data_ptr);

	(void) raid_obj_set_type(raid_tab, raid_obj->obj_id, obj_type);
	return (raid_obj->obj_id);
}

static int
raid_obj_delete(raid_obj_tab_t *raid_tab, raid_obj_id_t raid_obj_id)
{
	raid_obj_t *obj;

	obj = raid_obj_tab_remove(raid_tab, raid_obj_id);
	if (obj != NULL) {
		free(obj->data);
		free(obj);
		return (SUCCESS);
	}

	return (ERR_DEVICE_NOENT);
}

static int
raid_obj_add_org(raid_obj_tab_t *raid_tab, raid_obj_id_t obj_id,
	raid_obj_id_t container_id)
{
	raid_obj_id_t tmp, tmp1;

	tmp = raid_obj_get_comp(raid_tab, container_id);
	if (tmp < OBJ_NONE)
		return (ERR_DEVICE_NOENT);

	if (tmp == OBJ_NONE) {
		(void) raid_obj_set_container(raid_tab, obj_id, container_id);
		(void) raid_obj_set_comp(raid_tab, container_id, obj_id);
		return (SUCCESS);
	}

	while ((tmp1 = raid_obj_get_sibling(raid_tab, tmp)) != OBJ_NONE)
		tmp = tmp1;

	if (raid_obj_set_sibling(raid_tab, tmp, obj_id) < SUCCESS)
		return (ERR_DEVICE_NOENT);
	(void) raid_obj_set_container(raid_tab, obj_id, container_id);

	return (SUCCESS);
}

static raid_obj_type_id_t
raid_obj_get_type(raid_obj_tab_t *raid_tab, raid_obj_id_t obj_id)
{
	raid_obj_t *obj;

	obj = raid_obj_tab_find(raid_tab, obj_id);
	if (obj == NULL)
		return (ERR_DEVICE_NOENT);

	if ((obj->obj_type_id < OBJ_TYPE_SYSTEM) ||
	    (obj->obj_type_id >= OBJ_TYPE_ALL))
		return (ERR_DEVICE_INVALID);

	return (obj->obj_type_id);
}

static int
raid_obj_set_type(raid_obj_tab_t *raid_tab, raid_obj_id_t obj_id,
	raid_obj_type_id_t type)
{
	raid_obj_t *obj;

	obj = raid_obj_tab_find(raid_tab, obj_id);
	if (obj == NULL)
		return (ERR_DEVICE_NOENT);

	if ((type < OBJ_TYPE_SYSTEM) || (type >= OBJ_TYPE_ALL))
		return (ERR_DEVICE_TYPE);

	obj->obj_type_id = type;
	return (SUCCESS);
}

static raid_obj_status_t
raid_obj_get_status(raid_obj_tab_t *raid_tab, raid_obj_id_t obj_id)
{
	raid_obj_t *obj;

	obj = raid_obj_tab_find(raid_tab, obj_id);
	if (obj == NULL)
		return (ERR_DEVICE_NOENT);

	return (obj->status);
}

static int
raid_obj_set_status(raid_obj_tab_t *raid_tab, raid_obj_id_t obj_id,
	raid_obj_status_t status)
{
	raid_obj_t *obj;

	obj = raid_obj_tab_find(raid_tab, obj_id);
	if (obj == NULL)
		return (ERR_DEVICE_NOENT);

	obj->status = obj->status | status;

	return (SUCCESS);
}

static int
raid_obj_clear_status(raid_obj_tab_t *raid_tab, raid_obj_id_t obj_id,
	raid_obj_status_t status)
{
	raid_obj_t *obj;

	obj = raid_obj_tab_find(raid_tab, obj_id);
	if (obj == NULL)
		return (ERR_DEVICE_NOENT);

	obj->status = obj->status & ~status;

	return (SUCCESS);
}

static raid_obj_id_t
raid_obj_get_container(raid_obj_tab_t *raid_tab, raid_obj_id_t obj_id)
{
	raid_obj_t *obj;

	obj = raid_obj_tab_find(raid_tab, obj_id);
	if (obj == NULL)
		return (ERR_DEVICE_NOENT);

	return (obj->container);
}

static int
raid_obj_set_container(raid_obj_tab_t *raid_tab, raid_obj_id_t obj_id,
	raid_obj_id_t container_id)
{
	raid_obj_t *obj;

	obj = raid_obj_tab_find(raid_tab, obj_id);
	if (obj == NULL)
		return (ERR_DEVICE_NOENT);

	obj->container = container_id;
	return (SUCCESS);
}

static raid_obj_id_t
raid_obj_get_comp(raid_obj_tab_t *raid_tab, raid_obj_id_t obj_id)
{
	raid_obj_t *obj;

	obj = raid_obj_tab_find(raid_tab, obj_id);
	if (obj == NULL)
		return (ERR_DEVICE_NOENT);

	return (obj->component);
}

static int
raid_obj_set_comp(raid_obj_tab_t *raid_tab, raid_obj_id_t obj_id,
	raid_obj_id_t comp)
{
	raid_obj_t *obj;

	obj = raid_obj_tab_find(raid_tab, obj_id);
	if (obj == NULL)
		return (ERR_DEVICE_NOENT);

	obj->component = comp;
	return (SUCCESS);
}

static raid_obj_id_t
raid_obj_get_sibling(raid_obj_tab_t *raid_tab, raid_obj_id_t obj_id)
{
	raid_obj_t *obj;

	obj = raid_obj_tab_find(raid_tab, obj_id);
	if (obj == NULL)
		return (ERR_DEVICE_NOENT);

	return (obj->sibling);
}

static int
raid_obj_set_sibling(raid_obj_tab_t *raid_tab, raid_obj_id_t obj_id,
	raid_obj_id_t sibling)
{
	raid_obj_t *obj;

	obj = raid_obj_tab_find(raid_tab, obj_id);
	if (obj == NULL)
		return (ERR_DEVICE_NOENT);

	obj->sibling = sibling;

	return (SUCCESS);
}

static void *
raid_obj_get_data_ptr(raid_obj_tab_t *raid_tab, raid_obj_id_t obj_id)
{
	raid_obj_t *obj;

	obj = raid_obj_tab_find(raid_tab, obj_id);
	if (obj == NULL)
		return (NULL);

	return (obj->data);
}

static int
raid_obj_set_data_ptr(raid_obj_tab_t *raid_tab, raid_obj_id_t obj_id,
	void *data)
{
	raid_obj_t *obj;

	obj = raid_obj_tab_find(raid_tab, obj_id);
	if (obj == NULL)
		return (ERR_DEVICE_NOENT);

	obj->data = data;

	return (SUCCESS);
}

static raid_obj_handle_t
raid_obj_get_handle(raid_obj_tab_t *raid_tab, raid_obj_id_t obj_id)
{
	raid_obj_t *obj;

	obj = raid_obj_tab_find(raid_tab, obj_id);
	if (obj == NULL)
		return (ERR_DEVICE_NOENT);

	return (obj->handle);
}

static int
raid_obj_set_handle(raid_obj_tab_t *raid_tab, raid_obj_id_t obj_id,
	raid_obj_handle_t handle)
{
	raid_obj_t *obj;

	obj = raid_obj_tab_find(raid_tab, obj_id);
	if (obj == NULL)
		return (ERR_DEVICE_NOENT);

	obj->handle = handle;
	return (SUCCESS);
}
/*
 * Object list maintennance routines
 */
static void
raid_list_create(raid_list_t *list, size_t offset)
{
	list->head = NULL;
	list->tail = NULL;
	list->offset = offset;
}

static void *
raid_list_head(raid_list_t *list)
{
	return (list->head);
}

static void *
raid_list_next(raid_list_t *list, void *obj)
{
	raid_list_el_t *el = LIST_OBJ_TO_EL(list, obj);

	return (el->next);
}

static void
raid_list_insert_tail(raid_list_t *list, void *obj)
{
	raid_list_el_t *el = LIST_OBJ_TO_EL(list, obj), *el1;

	el->prev = list->tail;
	list->tail = obj;

	el->next = NULL;

	if (list->head == NULL)
		list->head = obj;

	if (el->prev != NULL) {
		el1 = LIST_OBJ_TO_EL(list, el->prev);
		el1->next = obj;
	}
}

static void
raid_list_remove(raid_list_t *list, void *obj)
{
	raid_list_el_t *el = LIST_OBJ_TO_EL(list, obj), *el1;

	if (list->head == obj)
		list->head = el->next;

	if (list->tail == obj)
		list->tail = el->prev;

	if (el->next != NULL) {
		el1 = LIST_OBJ_TO_EL(list, el->next);
		el1->prev = el->prev;
	}

	if (el->prev != NULL) {
		el1 = LIST_OBJ_TO_EL(list, el->prev);
		el1->next = el->next;
	}

	el->prev = el->next = NULL;
}

static void *
raid_list_remove_head(raid_list_t *list)
{
	void *obj = list->head;

	if (obj != NULL)
		raid_list_remove(list, obj);

	return (obj);
}

static void *
raid_list_find(raid_list_t *list, raid_obj_id_t obj_id)
{
	raid_obj_t *obj;

	for (obj = raid_list_head(list); obj != NULL;
	    obj = raid_list_next(list, obj))
			if (obj->obj_id == obj_id)
				break;

	return (obj);
}

static int
raid_obj_tab_create(raid_obj_tab_t *tab, size_t hash_slots)
{
	unsigned i;

	if (hash_slots == 0)
		return (ERR_OP_ILLEGAL);

	tab->slots = hash_slots;

	if ((tab->table = calloc(hash_slots, sizeof (raid_list_t))) == NULL)
		return (ERR_NOMEM);

	for (i = 0; i < hash_slots; i++)
		raid_list_create(&tab->table[i], offsetof(raid_obj_t, el));

	return (SUCCESS);
}

static void
raid_obj_tab_destroy(raid_obj_tab_t *tab)
{
	unsigned i;

	for (i = 0; i < tab->slots; i++) {
		struct raid_obj_t *obj;

		while ((obj = raid_list_remove_head(&tab->table[i])) != NULL)
			free(obj);

		raid_list_destroy(&tab->table[i]);
	}

	if (tab->table)
		free(tab->table);

	tab->table = NULL;
	tab->slots = 0;
	tab->obj_id_cnt = 0;
}

static int
raid_obj_tab_insert(raid_obj_tab_t *tab, raid_obj_id_t id, void *obj)
{
	raid_list_t *list;

	list = OBJ_TAB_SLOT(tab, id);

	if (raid_list_find(list, id) != NULL)
		return (ERR_DEVICE_DUP);

	raid_list_insert_tail(list, obj);

	return (SUCCESS);
}

static void *
raid_obj_tab_remove(raid_obj_tab_t *tab, raid_obj_id_t id)
{
	raid_list_t *list;
	raid_obj_t *obj;

	list = OBJ_TAB_SLOT(tab, id);

	if ((obj = raid_list_find(list, id)) != NULL)
		raid_list_remove(list, obj);

	return (obj);
}

static void *
raid_obj_tab_find(raid_obj_tab_t *tab, raid_obj_id_t id)
{
	raid_list_t *list;
	raid_obj_t *obj;

	list = OBJ_TAB_SLOT(tab, id);
	obj = raid_list_find(list, id);

	return (obj);
}

static void
raid_list_destroy(raid_list_t *list)
{
	list->head = NULL;
	list->tail = NULL;
	list->offset = 0;
}

/*
 * Plug-in maintennance routines
 */
static int
controller_id_to_path(uint32_t controller_id, char *path)
{
	char buf[MAX_PATH_LEN] = {0}, buf1[MAX_PATH_LEN] = {0}, *colon;

	(void) snprintf(buf, MAX_PATH_LEN, "%s/c%d", CFGDIR, controller_id);
	if (readlink(buf, buf1, sizeof (buf1)) < 0)
		return (ERR_DRIVER_NOT_FOUND);

	if (buf1[0] != '/')
		(void) snprintf(buf, sizeof (buf), "%s/", CFGDIR);
	else
		buf[0] = 0;
	(void) strlcat(buf, buf1, MAX_PATH_LEN);

	colon = strrchr(buf, ':');
	if (colon == NULL)
		return (ERR_DRIVER_NOT_FOUND);
	else
		*colon = 0;

	(void) snprintf(path, MAX_PATH_LEN, "%s:devctl", buf);

	if (access(path, F_OK) < 0)
		return (ERR_DRIVER_NOT_FOUND);

	return (SUCCESS);
}

static char *
controller_id_to_driver_name(uint32_t controller_id)
{
	char buf[MAX_PATH_LEN];
	di_node_t di_node;
	char *name, *tmp;
	int ret;

	ret = controller_id_to_path(controller_id, buf);
	if (ret < SUCCESS)
		return (NULL);

	tmp = strrchr(buf, ':');
	if (tmp != NULL)
		*tmp = 0;

	tmp = strstr(buf, "pci");
	if (tmp == NULL)
		return (NULL);

	di_node = di_init(tmp, DINFOPROP);
	if (di_node == DI_NODE_NIL)
		return (NULL);

	name = di_driver_name(di_node);

	return (name);
}

static void
raid_plugin_init()
{
	raid_lib_t *raid_lib = raid_lib_sys;

	while (raid_lib) {
		raid_lib_sys = raid_lib->next;
		(void) dlclose(raid_lib->lib_handle);
		free(raid_lib);
		raid_lib = raid_lib_sys;
	}
}

static raid_lib_t *
raid_plugin_load(char *driver_name)
{
	char buf[MAX_PATH_LEN] = {0};
	raid_lib_t *supplib;
	void *sym;

	supplib = calloc(1, sizeof (raid_lib_t));
	if (supplib == NULL)
		return (NULL);

	(void) snprintf(buf, MAX_PATH_LEN, "%s/%s.so.1",
	    SUPP_PLUGIN_DIR, driver_name);

	supplib->lib_handle = dlopen(buf, RTLD_LAZY);
	if (supplib->lib_handle == NULL) {
		free(supplib);
		return (NULL);
	}

	supplib->name = driver_name;

	if ((sym = dlsym(supplib->lib_handle, "rdcfg_version")) == NULL)
		supplib->version = RDCFG_PLUGIN_V1;
	else {
		supplib->version = *((uint32_t *)sym);
		if (supplib->version != RDCFG_PLUGIN_V1) {
			(void) dlclose(supplib->lib_handle);
			free(supplib);
			return (NULL);
		}
	}

	if ((sym = dlsym(supplib->lib_handle, "rdcfg_open_controller")) ==
	    NULL) {
		(void) dlclose(supplib->lib_handle);
		free(supplib);
		return (NULL);
	} else
		supplib->open_controller = (int(*)(uint32_t, char **))sym;

	if ((sym = dlsym(supplib->lib_handle, "rdcfg_close_controller")) ==
	    NULL) {
		(void) dlclose(supplib->lib_handle);
		free(supplib);
		return (NULL);
	} else
		supplib->close_controller = (int (*)(uint32_t, char **))sym;

	if ((sym = dlsym(supplib->lib_handle, "rdcfg_compnum")) == NULL) {
		(void) dlclose(supplib->lib_handle);
		free(supplib);
		return (NULL);
	} else
		supplib->compnum = (int (*)(uint32_t, uint32_t,
		    raid_obj_type_id_t, raid_obj_type_id_t))sym;

	if ((sym = dlsym(supplib->lib_handle, "rdcfg_complist")) == NULL) {
		(void) dlclose(supplib->lib_handle);
		free(supplib);
		return (NULL);
	} else
		supplib->complist = (int (*)(uint32_t, uint32_t,
		    raid_obj_type_id_t, raid_obj_type_id_t, int, void *))sym;

	if ((sym = dlsym(supplib->lib_handle, "rdcfg_get_attr")) == NULL) {
		(void) dlclose(supplib->lib_handle);
		free(supplib);
		return (NULL);
	} else
		supplib->get_attr = (int (*)(uint32_t, uint32_t, uint32_t,
		    raid_obj_type_id_t, void*))sym;

	if ((sym = dlsym(supplib->lib_handle, "rdcfg_array_create")) == NULL) {
		(void) dlclose(supplib->lib_handle);
		free(supplib);
		return (NULL);
	} else
		supplib->array_create = (int (*)(uint32_t, array_attr_t *, int,
		    arraypart_attr_t *, char **))sym;

	if ((sym = dlsym(supplib->lib_handle, "rdcfg_array_delete")) == NULL) {
		(void) dlclose(supplib->lib_handle);
		free(supplib);
		return (NULL);
	} else
		supplib->array_delete =
		    (int (*)(uint32_t, uint32_t, char **))sym;

	supplib->hsp_bind = (int (*)(uint32_t, uint32_t, hsp_relation_t *,
	    char **))dlsym(supplib->lib_handle, "rdcfg_hsp_bind");
	supplib->hsp_unbind = (int (*)(uint32_t, uint32_t, hsp_relation_t *,
	    char **))dlsym(supplib->lib_handle, "rdcfg_hsp_unbind");
	supplib->set_attr = (int (*)(uint32_t, uint32_t, uint32_t, uint32_t *,
	    char **))dlsym(supplib->lib_handle, "rdcfg_set_attr");
	supplib->flash_fw = (int (*)(uint32_t, char *, uint32_t, char **))
	    dlsym(supplib->lib_handle, "rdcfg_flash_fw");

	supplib->next = raid_lib_sys;
	raid_lib_sys = supplib;
	return (supplib);
}

static raid_lib_t *
raid_find_lib(raid_obj_tab_t *raid_tab, raid_obj_id_t controller_obj_id)
{
	controller_attr_t *controller_attr;
	raid_lib_t *raid_lib;
	char *driver_name;
	raid_obj_handle_t handle;

	/* Check if it's mapped to handle structure */
	handle = raid_obj_to_handle(raid_tab, controller_obj_id);
	if (raid_handle_sys.handles[handle].raid_lib != NULL)
		return (raid_handle_sys.handles[handle].raid_lib);

	(void) obj_get_attr(raid_tab, controller_obj_id,
	    (void **)(&controller_attr));

	/* Check if the plugin module is already loaded */
	driver_name = controller_id_to_driver_name(
	    controller_attr->controller_id);
	if (driver_name == NULL)
		return (NULL);

	raid_lib = raid_lib_sys;
	while (raid_lib != NULL) {
		if (raid_lib->name != NULL &&
		    strcmp(driver_name, raid_lib->name) == 0)
			return (raid_lib);

		raid_lib = raid_lib->next;
	}

	/* Loading the plugin module */
	raid_lib = raid_plugin_load(driver_name);

	return (raid_lib);
}