#include <sys/param.h>
#include <sys/kmem.h>
#include <sys/errno.h>
#include <sys/time.h>
#include <sys/devops.h>
#include <sys/ddi_impldefs.h>
#include <sys/cmn_err.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/proc.h>
#include <sys/thread.h>
#include <sys/taskq.h>
#include <sys/callb.h>
#include <sys/rsm/rsm.h>
#include <rsm_in.h>
#include <sys/rsm/rsmka_path_int.h>
extern void _cplpl_init();
extern void _cplpl_fini();
extern pri_t maxclsyspri;
extern int rsm_hash_size;
extern rsm_node_id_t my_nodeid;
extern rsmhash_table_t rsm_import_segs;
extern rsm_intr_hand_ret_t rsm_srv_func(rsm_controller_object_t *,
rsm_intr_q_op_t, rsm_addr_t, void *, size_t, rsm_intr_hand_arg_t);
extern void rsmseg_unload(rsmseg_t *);
extern void rsm_suspend_complete(rsm_node_id_t src_node, int flag);
extern int rsmipc_send_controlmsg(path_t *path, int msgtype);
extern void rsmka_path_monitor_initialize();
extern void rsmka_path_monitor_terminate();
extern adapter_t loopback_adapter;
static adapter_t *init_adapter(char *, int, rsm_addr_t,
rsm_controller_handle_t, rsm_ops_t *, srv_handler_arg_t *);
adapter_t *rsmka_lookup_adapter(char *, int);
static ipc_info_t *lookup_ipc_info(rsm_node_id_t);
static ipc_info_t *init_ipc_info(rsm_node_id_t, boolean_t);
static path_t *lookup_path(char *, int, rsm_node_id_t, rsm_addr_t);
static void pathup_to_pathactive(ipc_info_t *, rsm_node_id_t);
static void path_importer_disconnect(path_t *);
boolean_t rsmka_do_path_active(path_t *, int);
static boolean_t do_path_up(path_t *, int);
static void do_path_down(path_t *, int);
static void enqueue_work(work_token_t *);
static boolean_t cancel_work(work_token_t *);
static void link_path(path_t *);
static void destroy_path(path_t *);
static void link_sendq_token(sendq_token_t *, rsm_node_id_t);
static void unlink_sendq_token(sendq_token_t *, rsm_node_id_t);
boolean_t rsmka_check_node_alive(rsm_node_id_t);
static void do_deferred_work(caddr_t);
static int create_ipc_sendq(path_t *);
static void destroy_ipc_info(ipc_info_t *);
void rsmka_pathmanager_cleanup();
void rsmka_release_adapter(adapter_t *);
kt_did_t rsm_thread_id;
int rsmka_terminate_workthread_loop = 0;
static struct adapter_listhead_list adapter_listhead_base;
static work_queue_t work_queue;
static kmutex_t ipc_info_lock;
static ipc_info_t *ipc_info_head = NULL;
static int category = RSM_PATH_MANAGER | RSM_KERNEL_AGENT;
kmutex_t ipc_info_cvlock;
kcondvar_t ipc_info_cv;
void
rsmka_pathmanager_init()
{
kthread_t *tp;
DBG_PRINTF((category, RSM_DEBUG_VERBOSE,
"rsmka_pathmanager_init enter\n"));
mutex_init(&work_queue.work_mutex, NULL, MUTEX_DEFAULT, NULL);
mutex_init(&ipc_info_lock, NULL, MUTEX_DEFAULT, NULL);
mutex_init(&ipc_info_cvlock, NULL, MUTEX_DEFAULT, NULL);
mutex_init(&adapter_listhead_base.listlock, NULL,
MUTEX_DEFAULT, NULL);
cv_init(&work_queue.work_cv, NULL, CV_DEFAULT, NULL);
cv_init(&ipc_info_cv, NULL, CV_DEFAULT, NULL);
tp = thread_create(NULL, 0, do_deferred_work, NULL, 0, &p0,
TS_RUN, maxclsyspri);
rsm_thread_id = tp->t_did;
DBG_PRINTF((category, RSM_DEBUG_VERBOSE,
"rsmka_pathmanager_init done\n"));
}
void
rsmka_pathmanager_cleanup()
{
DBG_PRINTF((category, RSM_DEBUG_VERBOSE,
"rsmka_pathmanager_cleanup enter\n"));
ASSERT(work_queue.head == NULL);
mutex_enter(&work_queue.work_mutex);
rsmka_terminate_workthread_loop++;
cv_signal(&work_queue.work_cv);
mutex_exit(&work_queue.work_mutex);
if (rsm_thread_id)
thread_join(rsm_thread_id);
mutex_destroy(&work_queue.work_mutex);
cv_destroy(&work_queue.work_cv);
mutex_enter(&ipc_info_lock);
while (ipc_info_head)
destroy_ipc_info(ipc_info_head);
mutex_exit(&ipc_info_lock);
mutex_destroy(&ipc_info_lock);
mutex_destroy(&ipc_info_cvlock);
cv_destroy(&ipc_info_cv);
DBG_PRINTF((category, RSM_DEBUG_VERBOSE,
"rsmka_pathmanager_cleanup done\n"));
}
void
rsmka_set_my_nodeid(rsm_node_id_t local_nodeid)
{
my_nodeid = local_nodeid;
DBG_PRINTF((category, RSM_DEBUG_VERBOSE,
"rsm: node %d \n", my_nodeid));
}
static void
do_deferred_work(caddr_t arg )
{
adapter_t *adapter;
path_t *path;
work_token_t *work_token;
int work_opcode;
rsm_send_q_handle_t sendq_handle;
int error;
timespec_t tv;
callb_cpr_t cprinfo;
DBG_PRINTF((category, RSM_DEBUG_VERBOSE, "do_deferred_work enter\n"));
CALLB_CPR_INIT(&cprinfo, &work_queue.work_mutex, callb_generic_cpr,
"rsm_deferred_work");
for (;;) {
mutex_enter(&work_queue.work_mutex);
if (rsmka_terminate_workthread_loop) {
goto exit;
}
while (work_queue.head == NULL) {
CALLB_CPR_SAFE_BEGIN(&cprinfo);
cv_wait(&work_queue.work_cv, &work_queue.work_mutex);
CALLB_CPR_SAFE_END(&cprinfo, &work_queue.work_mutex);
if (rsmka_terminate_workthread_loop) {
goto exit;
}
}
work_token = work_queue.head;
work_queue.head = work_token->next;
if (work_queue.tail == work_token)
work_queue.tail = NULL;
work_opcode = work_token->opcode;
path = WORK_TOKEN_TO_PATH(work_token, work_opcode -1);
work_token->next = NULL;
mutex_exit(&work_queue.work_mutex);
switch (work_opcode) {
case RSMKA_IPC_UP:
DBG_PRINTF((category, RSM_DEBUG,
"do_deferred_work:up, path = %lx\n", path));
error = create_ipc_sendq(path);
mutex_enter(&path->mutex);
if (path->state != RSMKA_PATH_UP) {
if (error == RSM_SUCCESS) {
sendq_handle = path->sendq_token.
rsmpi_sendq_handle;
path->sendq_token.rsmpi_sendq_handle =
NULL;
adapter = path->local_adapter;
mutex_exit(&path->mutex);
if (sendq_handle != NULL) {
adapter->rsmpi_ops->
rsm_sendq_destroy(
sendq_handle);
}
mutex_enter(&path->mutex);
}
work_token->opcode = 0;
PATH_RELE_NOLOCK(path);
mutex_exit(&path->mutex);
break;
}
if (error == RSM_SUCCESS) {
DBG_PRINTF((category, RSM_DEBUG,
"do_deferred_work:success on up\n"));
path->flags &= ~RSMKA_SQCREATE_PENDING;
path->state = RSMKA_PATH_ACTIVE;
path->procmsg_cnt = 0;
path->sendq_token.msgbuf_avail = 0;
gethrestime(&tv);
if (tv.tv_sec == RSM_UNKNOWN_INCN)
tv.tv_sec = 1;
path->local_incn = (int64_t)tv.tv_sec;
(void) rsmipc_send_controlmsg(path,
RSMIPC_MSG_SQREADY);
path->flags |= RSMKA_WAIT_FOR_SQACK;
} else {
path->flags |= RSMKA_SQCREATE_PENDING;
}
work_token->opcode = 0;
PATH_RELE_NOLOCK(path);
mutex_exit(&path->mutex);
break;
case RSMKA_IPC_DOWN:
DBG_PRINTF((category, RSM_DEBUG,
"do_deferred_work:down, path = %lx\n", path));
mutex_enter(&path->mutex);
path->state = RSMKA_PATH_DOWN;
path->flags &= ~RSMKA_WAIT_FOR_SQACK;
cv_broadcast(&path->sendq_token.sendq_cv);
mutex_exit(&path->mutex);
taskq_wait(path->recv_taskq);
path_importer_disconnect(path);
DBG_PRINTF((category, RSM_DEBUG,
"do_deferred_work: success on down\n"));
mutex_enter(&path->mutex);
#ifdef DEBUG
if (path->msgbuf_cnt != 0)
cmn_err(CE_NOTE,
"path=%lx msgbuf_cnt != 0\n",
(uintptr_t)path);
#endif
while (path->sendq_token.ref_cnt != 0)
cv_wait(&path->sendq_token.sendq_cv,
&path->mutex);
sendq_handle = path->sendq_token.rsmpi_sendq_handle;
path->sendq_token.rsmpi_sendq_handle = NULL;
if (sendq_handle != NULL) {
adapter = path->local_adapter;
adapter->rsmpi_ops->rsm_sendq_destroy(
sendq_handle);
}
work_token->opcode = 0;
PATH_RELE_NOLOCK(path);
mutex_exit(&path->mutex);
break;
default:
DBG_PRINTF((category, RSM_DEBUG,
"do_deferred_work: bad work token opcode\n"));
break;
}
}
exit:
DBG_PRINTF((category, RSM_DEBUG_VERBOSE, "do_deferred_work done\n"));
CALLB_CPR_EXIT(&cprinfo);
}
static void
enqueue_work(work_token_t *token)
{
work_token_t *tail_token;
DBG_PRINTF((category, RSM_DEBUG_VERBOSE, "enqueue_work enter\n"));
ASSERT(MUTEX_HELD(&work_queue.work_mutex));
token->next = NULL;
if (work_queue.head == NULL) {
work_queue.head = work_queue.tail = token;
} else {
tail_token = work_queue.tail;
work_queue.tail = tail_token->next = token;
}
cv_signal(&work_queue.work_cv);
DBG_PRINTF((category, RSM_DEBUG_VERBOSE, "enqueue_work done\n"));
}
static boolean_t
cancel_work(work_token_t *work_token)
{
work_token_t *current_token;
work_token_t *prev_token = NULL;
boolean_t cancelled = B_FALSE;
DBG_PRINTF((category, RSM_DEBUG_VERBOSE, "cancel_work enter\n"));
ASSERT(MUTEX_HELD(&work_queue.work_mutex));
current_token = work_queue.head;
while (current_token != NULL) {
if (current_token == work_token) {
if (work_token == work_queue.head)
work_queue.head = work_token->next;
else
prev_token->next = work_token->next;
if (work_token == work_queue.tail)
work_queue.tail = prev_token;
current_token->opcode = 0;
current_token->next = NULL;
cancelled = B_TRUE;
DBG_PRINTF((category, RSM_DEBUG,
"cancelled_work = 0x%p\n", work_token));
break;
}
prev_token = current_token;
current_token = current_token->next;
}
DBG_PRINTF((category, RSM_DEBUG_VERBOSE, "cancel_work done\n"));
return (cancelled);
}
void *
rsmka_add_adapter(char *name, int instance, rsm_addr_t hwaddr)
{
adapter_t *adapter;
rsm_controller_object_t rsmpi_adapter_object;
rsm_controller_handle_t rsmpi_adapter_handle;
rsm_ops_t *rsmpi_ops_vector;
int adapter_is_supported;
rsm_controller_attr_t *attr;
srv_handler_arg_t *srv_hdlr_argp;
int result;
DBG_PRINTF((category, RSM_DEBUG_VERBOSE, "rsmka_add_adapter enter\n"));
DBG_PRINTF((category, RSM_DEBUG,
"rsmka_add_adapter: name = %s instance = %d hwaddr = %llx \n",
name, instance, hwaddr));
if (strlen(name) >= MAXNAMELEN) {
DBG_PRINTF((category, RSM_DEBUG_VERBOSE,
"rsmka_add_adapter done: name too long\n"));
return (NULL);
}
adapter_is_supported = rsm_get_controller(name, instance,
&rsmpi_adapter_object, RSM_VERSION);
if (adapter_is_supported != RSM_SUCCESS) {
DBG_PRINTF((category, RSM_ERR,
"rsmka_add_adapter done: adapter not supported\n"));
return (NULL);
}
rsmpi_adapter_handle = rsmpi_adapter_object.handle;
rsmpi_ops_vector = rsmpi_adapter_object.ops;
result = rsm_get_controller_attr(rsmpi_adapter_handle, &attr);
if (result != RSM_SUCCESS) {
DBG_PRINTF((category, RSM_ERR,
"rsm: get_controller_attr(%d) Failed %x\n",
instance, result));
(void) rsm_release_controller(name, instance,
&rsmpi_adapter_object);
return (NULL);
}
DBG_PRINTF((category, RSM_DEBUG,
"rsmka_add_adapter: register service offset = %d\n", hwaddr));
srv_hdlr_argp = kmem_zalloc(sizeof (srv_handler_arg_t), KM_SLEEP);
(void) strcpy(srv_hdlr_argp->adapter_name, name);
srv_hdlr_argp->adapter_instance = instance;
srv_hdlr_argp->adapter_hwaddr = hwaddr;
result = rsmpi_ops_vector->rsm_register_handler(
rsmpi_adapter_handle, &rsmpi_adapter_object,
RSM_SERVICE+(uint_t)hwaddr, rsm_srv_func,
(rsm_intr_hand_arg_t)srv_hdlr_argp, NULL, 0);
if (result != RSM_SUCCESS) {
DBG_PRINTF((category, RSM_ERR,
"rsmka_add_adapter done: rsm_register_handler"
"failed %d\n",
instance));
return (NULL);
}
adapter = init_adapter(name, instance, hwaddr,
rsmpi_adapter_handle, rsmpi_ops_vector, srv_hdlr_argp);
adapter->rsm_attr = *attr;
DBG_PRINTF((category, RSM_ERR,
"rsmka_add_adapter: adapter = %lx\n", adapter));
DBG_PRINTF((category, RSM_DEBUG_VERBOSE, "rsmka_add_adapter done\n"));
return ((void *)adapter);
}
boolean_t
rsmka_remove_adapter(char *name, uint_t instance, void *cookie, int flags)
{
adapter_t *adapter;
adapter_listhead_t *listhead;
adapter_t *prev, *current;
rsm_controller_object_t rsm_cntl_obj;
DBG_PRINTF((category, RSM_DEBUG_VERBOSE,
"rsmka_remove_adapter enter\n"));
DBG_PRINTF((category, RSM_DEBUG_VERBOSE,
"rsmka_remove_adapter: cookie = %lx\n", cookie));
if (flags & RSMKA_USE_COOKIE) {
adapter = (adapter_t *)cookie;
} else {
adapter = rsmka_lookup_adapter(name, instance);
ADAPTER_RELE(adapter);
}
ASSERT(adapter->next_path == NULL);
listhead = adapter->listhead;
mutex_enter(&adapter_listhead_base.listlock);
mutex_enter(&listhead->mutex);
prev = NULL;
current = listhead->next_adapter;
while (current != NULL) {
if (adapter->instance == current->instance) {
break;
} else {
prev = current;
current = current->next;
}
}
ASSERT(current != NULL);
if (prev == NULL)
listhead->next_adapter = current->next;
else
prev->next = current->next;
listhead->adapter_count--;
mutex_exit(&listhead->mutex);
mutex_exit(&adapter_listhead_base.listlock);
mutex_enter(¤t->mutex);
current->rsmpi_ops->rsm_unregister_handler(current->rsmpi_handle,
RSM_SERVICE+current->hwaddr, rsm_srv_func,
(rsm_intr_hand_arg_t)current->hdlr_argp);
DBG_PRINTF((category, RSM_DEBUG, "rsmka_remove_adapter: unreg hdlr "
":adapter=%lx, hwaddr=%lx\n", current, current->hwaddr));
rsm_cntl_obj.handle = current->rsmpi_handle;
rsm_cntl_obj.ops = current->rsmpi_ops;
(void) rsm_release_controller(current->listhead->adapter_devname,
current->instance, &rsm_cntl_obj);
mutex_exit(¤t->mutex);
rsmka_release_adapter(current);
DBG_PRINTF((category, RSM_DEBUG_VERBOSE,
"rsmka_remove_adapter done\n"));
return (B_TRUE);
}
void *
rsmka_add_path(char *adapter_name, int adapter_instance,
rsm_node_id_t remote_node,
rsm_addr_t remote_hwaddr, int rem_adapt_instance,
void *cookie, int flags)
{
path_t *path;
adapter_t *adapter;
char tq_name[TASKQ_NAMELEN];
DBG_PRINTF((category, RSM_DEBUG_VERBOSE, "rsmka_add_path enter\n"));
path = kmem_zalloc(sizeof (path_t), KM_SLEEP);
if (flags & RSMKA_USE_COOKIE) {
adapter = (adapter_t *)cookie;
ADAPTER_HOLD(adapter);
} else {
adapter = rsmka_lookup_adapter(adapter_name, adapter_instance);
}
DBG_PRINTF((category, RSM_DEBUG,
"rsmka_add_path: adapter = %lx\n", adapter));
mutex_init(&path->mutex, NULL, MUTEX_DEFAULT, NULL);
PATH_HOLD(path);
path->state = RSMKA_PATH_DOWN;
path->remote_node = remote_node;
path->remote_hwaddr = remote_hwaddr;
path->remote_devinst = rem_adapt_instance;
path->local_adapter = adapter;
(void) snprintf(tq_name, sizeof (tq_name), "%x_%llx",
remote_node, (unsigned long long) remote_hwaddr);
path->recv_taskq = taskq_create_instance(tq_name, adapter_instance,
RSMKA_ONE_THREAD, maxclsyspri, RSMIPC_MAX_MESSAGES,
RSMIPC_MAX_MESSAGES, TASKQ_PREPOPULATE);
path->msgbuf_queue = (msgbuf_elem_t *)kmem_zalloc(
RSMIPC_MAX_MESSAGES * sizeof (msgbuf_elem_t), KM_SLEEP);
cv_init(&path->sendq_token.sendq_cv, NULL, CV_DEFAULT, NULL);
cv_init(&path->hold_cv, NULL, CV_DEFAULT, NULL);
link_path(path);
link_sendq_token(&path->sendq_token, remote_node);
ADAPTER_RELE(adapter);
DBG_PRINTF((category, RSM_DEBUG, "rsmka_add_path: path = %lx\n", path));
DBG_PRINTF((category, RSM_DEBUG_VERBOSE, "rsmka_add_path done\n"));
return ((void *)path);
}
void
rsmka_remove_path(char *adapter_name, int instance, rsm_node_id_t remote_node,
rsm_addr_t remote_hwaddr, void *path_cookie, int flags)
{
path_t *path;
DBG_PRINTF((category, RSM_DEBUG_VERBOSE, "rsmka_remove_path enter\n"));
if (flags & RSMKA_USE_COOKIE) {
path = (path_t *)path_cookie;
mutex_enter(&path->mutex);
} else {
path = lookup_path(adapter_name, instance, remote_node,
remote_hwaddr);
PATH_RELE_NOLOCK(path);
}
DBG_PRINTF((category, RSM_DEBUG,
"rsmka_remove_path: path = %lx\n", path));
while (path->state == RSMKA_PATH_GOING_DOWN)
cv_wait(&path->hold_cv, &path->mutex);
mutex_enter(&work_queue.work_mutex);
if (cancel_work(&path->work_token[RSMKA_IPC_UP_INDEX])) {
PATH_RELE_NOLOCK(path);
}
if (cancel_work(&path->work_token[RSMKA_IPC_DOWN_INDEX])) {
PATH_RELE_NOLOCK(path);
}
mutex_exit(&work_queue.work_mutex);
PATH_RELE_NOLOCK(path);
switch (path->state) {
case RSMKA_PATH_UP:
path->flags &= ~RSMKA_SQCREATE_PENDING;
path->state = RSMKA_PATH_DOWN;
break;
case RSMKA_PATH_DOWN:
break;
case RSMKA_PATH_ACTIVE:
do_path_down(path, 0);
break;
default:
mutex_exit(&path->mutex);
DBG_PRINTF((category, RSM_ERR,
"rsm_remove_path: invalid path state %d\n",
path->state));
return;
}
while (path->ref_cnt != 0) {
cv_wait(&path->hold_cv, &path->mutex);
}
mutex_exit(&path->mutex);
unlink_sendq_token(&path->sendq_token, path->remote_node);
destroy_path(path);
DBG_PRINTF((category, RSM_DEBUG_VERBOSE, "rsmka_remove_path done\n"));
}
boolean_t
rsmka_path_up(char *adapter_name, uint_t adapter_instance,
rsm_node_id_t remote_node, rsm_addr_t remote_hwaddr,
void *path_cookie, int flags)
{
path_t *path;
boolean_t rval = B_TRUE;
DBG_PRINTF((category, RSM_DEBUG_VERBOSE, "rsmka_path_up enter\n"));
if (flags & RSMKA_USE_COOKIE) {
path = (path_t *)path_cookie;
mutex_enter(&path->mutex);
PATH_HOLD_NOLOCK(path);
} else {
path = lookup_path(adapter_name, adapter_instance,
remote_node, remote_hwaddr);
}
while (path->state == RSMKA_PATH_GOING_DOWN)
cv_wait(&path->hold_cv, &path->mutex);
DBG_PRINTF((category, RSM_DEBUG, "rsmka_path_up: path = %lx\n", path));
rval = do_path_up(path, flags);
mutex_exit(&path->mutex);
DBG_PRINTF((category, RSM_DEBUG_VERBOSE, "rsmka_path_up done\n"));
return (rval);
}
boolean_t
rsmka_path_down(char *adapter_devname, int instance, rsm_node_id_t remote_node,
rsm_addr_t remote_hwaddr, void *path_cookie, int flags)
{
path_t *path;
boolean_t rval = B_TRUE;
DBG_PRINTF((category, RSM_DEBUG_VERBOSE, "rsmka_path_down enter\n"));
if (flags & RSMKA_USE_COOKIE) {
path = (path_t *)path_cookie;
mutex_enter(&path->mutex);
PATH_HOLD_NOLOCK(path);
} else {
path = lookup_path(adapter_devname, instance, remote_node,
remote_hwaddr);
}
while (path->state == RSMKA_PATH_GOING_DOWN)
cv_wait(&path->hold_cv, &path->mutex);
DBG_PRINTF((category, RSM_DEBUG,
"rsmka_path_down: path = %lx\n", path));
switch (path->state) {
case RSMKA_PATH_UP:
path->flags &= ~RSMKA_SQCREATE_PENDING;
path->state = RSMKA_PATH_GOING_DOWN;
mutex_exit(&path->mutex);
taskq_wait(path->recv_taskq);
mutex_enter(&path->mutex);
path->state = RSMKA_PATH_DOWN;
PATH_RELE_NOLOCK(path);
break;
case RSMKA_PATH_DOWN:
PATH_RELE_NOLOCK(path);
break;
case RSMKA_PATH_ACTIVE:
do_path_down(path, flags);
if (!(flags & RSMKA_NO_SLEEP))
PATH_RELE_NOLOCK(path);
break;
default:
DBG_PRINTF((category, RSM_ERR,
"rsm_path_down: invalid path state %d\n", path->state));
rval = B_FALSE;
}
mutex_exit(&path->mutex);
DBG_PRINTF((category, RSM_DEBUG_VERBOSE, "rsmka_path_down done\n"));
return (rval);
}
boolean_t
rsmka_node_alive(rsm_node_id_t remote_node)
{
ipc_info_t *ipc_info;
DBG_PRINTF((category, RSM_DEBUG_VERBOSE, "rsmka_node_alive enter\n"));
DBG_PRINTF((category, RSM_DEBUG,
"rsmka_node_alive: remote_node = %x\n", remote_node));
ipc_info = lookup_ipc_info(remote_node);
if (ipc_info == NULL) {
ipc_info = init_ipc_info(remote_node, B_TRUE);
DBG_PRINTF((category, RSM_DEBUG,
"rsmka_node_alive: new ipc_info = %lx\n", ipc_info));
} else {
ASSERT(ipc_info->node_is_alive == B_FALSE);
ipc_info->node_is_alive = B_TRUE;
}
pathup_to_pathactive(ipc_info, remote_node);
mutex_exit(&ipc_info_lock);
mutex_enter(&ipc_info_cvlock);
cv_broadcast(&ipc_info_cv);
mutex_exit(&ipc_info_cvlock);
DBG_PRINTF((category, RSM_DEBUG_VERBOSE, "rsmka_node_alive done\n"));
return (B_TRUE);
}
boolean_t
rsmka_node_died(rsm_node_id_t remote_node)
{
ipc_info_t *ipc_info;
DBG_PRINTF((category, RSM_DEBUG_VERBOSE, "rsmka_node_died enter\n"));
DBG_PRINTF((category, RSM_DEBUG,
"rsmka_node_died: remote_node = %x\n", remote_node));
ipc_info = lookup_ipc_info(remote_node);
if (ipc_info == NULL)
return (B_FALSE);
ASSERT(ipc_info->node_is_alive == B_TRUE);
ipc_info->node_is_alive = B_FALSE;
rsm_suspend_complete(remote_node, RSM_SUSPEND_NODEDEAD);
mutex_exit(&ipc_info_lock);
mutex_enter(&ipc_info_cvlock);
cv_broadcast(&ipc_info_cv);
mutex_exit(&ipc_info_cvlock);
DBG_PRINTF((category, RSM_DEBUG_VERBOSE, "rsmka_node_died done\n"));
return (B_TRUE);
}
void
rsmka_disconnect_node(rsm_node_id_t remote_node, int flags)
{
ipc_info_t *ipc_info;
path_t *path;
sendq_token_t *sendq_token;
work_token_t *up_token;
work_token_t *down_token;
boolean_t do_work = B_FALSE;
boolean_t cancelled = B_FALSE;
DBG_PRINTF((category, RSM_DEBUG_VERBOSE,
"rsmka_disconnect_node enter\n"));
DBG_PRINTF((category, RSM_DEBUG,
"rsmka_disconnect_node: node = %d\n", remote_node));
if (flags & RSMKA_NO_SLEEP) {
ipc_info = lookup_ipc_info(remote_node);
sendq_token = ipc_info->token_list;
while (sendq_token != NULL) {
path = SQ_TOKEN_TO_PATH(sendq_token);
PATH_HOLD(path);
up_token = &path->work_token[RSMKA_IPC_UP_INDEX];
down_token = &path->work_token[RSMKA_IPC_DOWN_INDEX];
mutex_enter(&work_queue.work_mutex);
cancelled = cancel_work(up_token);
if (path->state == RSMKA_PATH_ACTIVE) {
if (down_token->opcode == 0)
do_work = B_TRUE;
} else
if (up_token->opcode == RSMKA_IPC_UP)
do_work = B_TRUE;
if (do_work == B_TRUE) {
down_token->opcode = RSMKA_IPC_DOWN;
enqueue_work(down_token);
}
mutex_exit(&work_queue.work_mutex);
if (do_work == B_FALSE)
PATH_RELE(path);
if (cancelled) {
PATH_RELE(path);
}
sendq_token = sendq_token->next;
}
mutex_enter(&work_queue.work_mutex);
cv_signal(&work_queue.work_cv);
mutex_exit(&work_queue.work_mutex);
IPCINFO_RELE_NOLOCK(ipc_info);
mutex_exit(&ipc_info_lock);
} else {
ipc_info = lookup_ipc_info(remote_node);
sendq_token = ipc_info->token_list;
while (sendq_token != NULL) {
path = SQ_TOKEN_TO_PATH(sendq_token);
DBG_PRINTF((category, RSM_DEBUG,
"rsmka_disconnect_node: path_down"
"for path = %x\n",
path));
(void) rsmka_path_down(0, 0, 0, 0,
path, RSMKA_USE_COOKIE);
sendq_token = sendq_token->next;
if (sendq_token == ipc_info->token_list)
break;
}
mutex_exit(&ipc_info_lock);
}
DBG_PRINTF((category, RSM_DEBUG_VERBOSE,
"rsmka_disconnect_node done\n"));
}
static void
pathup_to_pathactive(ipc_info_t *ipc_info, rsm_node_id_t remote_node)
{
path_t *path;
sendq_token_t *token;
DBG_PRINTF((category, RSM_DEBUG_VERBOSE,
"pathup_to_pathactive enter\n"));
remote_node = remote_node;
ASSERT(MUTEX_HELD(&ipc_info_lock));
token = ipc_info->token_list;
while (token != NULL) {
path = SQ_TOKEN_TO_PATH(token);
mutex_enter(&path->mutex);
if (path->state == RSMKA_PATH_UP) {
PATH_HOLD_NOLOCK(path);
(void) rsmka_do_path_active(path, 0);
}
mutex_exit(&path->mutex);
token = token->next;
if (token == ipc_info->token_list)
break;
}
DBG_PRINTF((category, RSM_DEBUG_VERBOSE,
"pathup_to_pathactive done\n"));
}
boolean_t
rsmka_do_path_active(path_t *path, int flags)
{
work_token_t *up_token = &path->work_token[RSMKA_IPC_UP_INDEX];
work_token_t *down_token = &path->work_token[RSMKA_IPC_DOWN_INDEX];
boolean_t do_work = B_FALSE;
int error;
timespec_t tv;
adapter_t *adapter;
rsm_send_q_handle_t sqhdl;
DBG_PRINTF((category, RSM_DEBUG_VERBOSE,
"rsmka_do_path_active enter\n"));
ASSERT(MUTEX_HELD(&path->mutex));
if (flags & RSMKA_NO_SLEEP) {
mutex_enter(&work_queue.work_mutex);
if (cancel_work(down_token)) {
PATH_RELE_NOLOCK(path);
}
if (path->state != RSMKA_PATH_ACTIVE) {
if (up_token->opcode == 0)
do_work = B_TRUE;
} else
if (down_token->opcode == RSMKA_IPC_DOWN)
do_work = B_TRUE;
if (do_work == B_TRUE) {
up_token->opcode = RSMKA_IPC_UP;
enqueue_work(up_token);
}
else
PATH_RELE_NOLOCK(path);
mutex_exit(&work_queue.work_mutex);
DBG_PRINTF((category, RSM_DEBUG_VERBOSE,
"rsmka_do_path_active done\n"));
return (B_TRUE);
} else {
mutex_exit(&path->mutex);
error = create_ipc_sendq(path);
mutex_enter(&path->mutex);
if (path->state != RSMKA_PATH_UP) {
if (error == RSM_SUCCESS) {
sqhdl = path->sendq_token.rsmpi_sendq_handle;
path->sendq_token.rsmpi_sendq_handle = NULL;
adapter = path->local_adapter;
mutex_exit(&path->mutex);
if (sqhdl != NULL) {
adapter->rsmpi_ops->rsm_sendq_destroy(
sqhdl);
}
mutex_enter(&path->mutex);
}
PATH_RELE_NOLOCK(path);
DBG_PRINTF((category, RSM_DEBUG_VERBOSE,
"rsmka_do_path_active done: path=%lx not UP\n",
(uintptr_t)path));
return (error ? B_FALSE : B_TRUE);
}
if (error == RSM_SUCCESS) {
path->flags &= ~RSMKA_SQCREATE_PENDING;
path->state = RSMKA_PATH_ACTIVE;
path->procmsg_cnt = 0;
path->sendq_token.msgbuf_avail = 0;
gethrestime(&tv);
if (tv.tv_sec == RSM_UNKNOWN_INCN)
tv.tv_sec = 1;
path->local_incn = (int64_t)tv.tv_sec;
(void) rsmipc_send_controlmsg(path, RSMIPC_MSG_SQREADY);
path->flags |= RSMKA_WAIT_FOR_SQACK;
DBG_PRINTF((category, RSM_DEBUG,
"rsmka_do_path_active success\n"));
} else {
path->flags |= RSMKA_SQCREATE_PENDING;
}
PATH_RELE_NOLOCK(path);
DBG_PRINTF((category, RSM_DEBUG_VERBOSE,
"rsmka_do_path_active done\n"));
return (error ? B_FALSE : B_TRUE);
}
}
static boolean_t
do_path_up(path_t *path, int flags)
{
boolean_t rval;
boolean_t node_alive;
DBG_PRINTF((category, RSM_DEBUG_VERBOSE, "do_path_up enter\n"));
ASSERT(MUTEX_HELD(&path->mutex));
if (path->state == RSMKA_PATH_ACTIVE) {
DBG_PRINTF((category, RSM_DEBUG_VERBOSE,
"do_path_up done: already ACTIVE\n"));
PATH_RELE_NOLOCK(path);
return (B_TRUE);
}
path->state = RSMKA_PATH_UP;
path->msgbuf_head = 0;
path->msgbuf_tail = RSMIPC_MAX_MESSAGES - 1;
path->msgbuf_cnt = 0;
path->procmsg_cnt = 0;
mutex_exit(&path->mutex);
node_alive = rsmka_check_node_alive(path->remote_node);
mutex_enter(&path->mutex);
if (node_alive == B_TRUE)
rval = rsmka_do_path_active(path, flags);
else {
PATH_RELE_NOLOCK(path);
rval = B_TRUE;
}
DBG_PRINTF((category, RSM_DEBUG_VERBOSE, "do_path_up done\n"));
return (rval);
}
static void
do_path_down(path_t *path, int flags)
{
work_token_t *up_token = &path->work_token[RSMKA_IPC_UP_INDEX];
work_token_t *down_token = &path->work_token[RSMKA_IPC_DOWN_INDEX];
boolean_t do_work = B_FALSE;
DBG_PRINTF((category, RSM_DEBUG_VERBOSE, "do_path_down enter\n"));
ASSERT(MUTEX_HELD(&path->mutex));
if (flags & RSMKA_NO_SLEEP) {
mutex_enter(&work_queue.work_mutex);
DBG_PRINTF((category, RSM_DEBUG,
"do_path_down: after work_mutex\n"));
if (cancel_work(up_token)) {
PATH_RELE_NOLOCK(path);
}
if (path->state == RSMKA_PATH_ACTIVE) {
if (down_token->opcode == 0)
do_work = B_TRUE;
} else
if (up_token->opcode == RSMKA_IPC_UP)
do_work = B_TRUE;
if (do_work == B_TRUE) {
down_token->opcode = RSMKA_IPC_DOWN;
enqueue_work(down_token);
} else
PATH_RELE_NOLOCK(path);
mutex_exit(&work_queue.work_mutex);
} else {
path->state = RSMKA_PATH_GOING_DOWN;
path->flags &= ~RSMKA_WAIT_FOR_SQACK;
cv_broadcast(&path->sendq_token.sendq_cv);
mutex_exit(&path->mutex);
taskq_wait(path->recv_taskq);
path_importer_disconnect(path);
mutex_enter(&path->mutex);
#ifdef DEBUG
if (path->msgbuf_cnt != 0)
cmn_err(CE_NOTE, "path=%lx msgbuf_cnt != 0\n",
(uintptr_t)path);
#endif
while (path->sendq_token.ref_cnt != 0)
cv_wait(&path->sendq_token.sendq_cv,
&path->mutex);
if (path->sendq_token.rsmpi_sendq_handle != NULL)
path->local_adapter->rsmpi_ops->rsm_sendq_destroy(
path->sendq_token.rsmpi_sendq_handle);
path->sendq_token.rsmpi_sendq_handle = NULL;
path->state = RSMKA_PATH_DOWN;
cv_signal(&path->hold_cv);
}
DBG_PRINTF((category, RSM_DEBUG_VERBOSE, "do_path_down done\n"));
}
static void
path_importer_disconnect(path_t *path)
{
int i;
adapter_t *adapter = path->local_adapter;
rsm_node_id_t remote_node = path->remote_node;
rsmresource_t *p = NULL;
rsmseg_t *seg;
DBG_PRINTF((category, RSM_DEBUG_VERBOSE,
"path_importer_disconnect enter\n"));
rw_enter(&rsm_import_segs.rsmhash_rw, RW_READER);
if (rsm_import_segs.bucket != NULL) {
for (i = 0; i < rsm_hash_size; i++) {
p = rsm_import_segs.bucket[i];
for (; p; p = p->rsmrc_next) {
if ((p->rsmrc_node == remote_node) &&
(p->rsmrc_adapter == adapter)) {
seg = (rsmseg_t *)p;
rsmseglock_acquire(seg);
seg->s_flags |= RSM_FORCE_DISCONNECT;
rsmseg_unload(seg);
}
}
}
}
rw_exit(&rsm_import_segs.rsmhash_rw);
DBG_PRINTF((category, RSM_DEBUG_VERBOSE,
"path_importer_disconnect done\n"));
}
static adapter_listhead_t *
init_listhead(char *name)
{
adapter_listhead_t *listhead;
DBG_PRINTF((category, RSM_DEBUG_VERBOSE, "init_listhead enter\n"));
listhead = kmem_zalloc(sizeof (adapter_listhead_t), KM_SLEEP);
mutex_init(&listhead->mutex, NULL, MUTEX_DEFAULT, NULL);
(void) strcpy(listhead->adapter_devname, name);
mutex_enter(&adapter_listhead_base.listlock);
if (adapter_listhead_base.next == NULL) {
adapter_listhead_base.next = listhead;
listhead->next_listhead = NULL;
} else {
listhead->next_listhead = adapter_listhead_base.next;
adapter_listhead_base.next = listhead;
}
mutex_exit(&adapter_listhead_base.listlock);
DBG_PRINTF((category, RSM_DEBUG_VERBOSE, "init_listhead done\n"));
return (listhead);
}
static adapter_listhead_t *
lookup_adapter_listhead(char *name)
{
adapter_listhead_t *listhead;
DBG_PRINTF((category, RSM_DEBUG_VERBOSE,
"lookup_adapter_listhead enter\n"));
mutex_enter(&adapter_listhead_base.listlock);
listhead = adapter_listhead_base.next;
while (listhead != NULL) {
if (strcmp(name, listhead->adapter_devname) == 0)
break;
listhead = listhead->next_listhead;
}
mutex_exit(&adapter_listhead_base.listlock);
DBG_PRINTF((category, RSM_DEBUG_VERBOSE,
"lookup_adapter_listhead done\n"));
return (listhead);
}
adapter_t *
rsmka_lookup_adapter(char *devname, int instance)
{
adapter_listhead_t *listhead;
adapter_t *current = NULL;
DBG_PRINTF((category, RSM_DEBUG_VERBOSE,
"rsmka_lookup_adapter enter\n"));
listhead = lookup_adapter_listhead(devname);
if (listhead != NULL) {
mutex_enter(&listhead->mutex);
current = listhead->next_adapter;
while (current != NULL) {
if (current->instance == instance) {
ADAPTER_HOLD(current);
break;
} else
current = current->next;
}
mutex_exit(&listhead->mutex);
}
DBG_PRINTF((category, RSM_DEBUG_VERBOSE,
"rsmka_lookup_adapter done\n"));
return (current);
}
void
rsmka_release_adapter(adapter_t *adapter)
{
DBG_PRINTF((category, RSM_DEBUG_VERBOSE,
"rsmka_release_adapter enter\n"));
if (adapter == &loopback_adapter) {
DBG_PRINTF((category, RSM_DEBUG_VERBOSE,
"rsmka_release_adapter done\n"));
return;
}
mutex_enter(&adapter->mutex);
ADAPTER_RELE_NOLOCK(adapter);
if (adapter->ref_cnt == 1) {
mutex_exit(&adapter->mutex);
mutex_destroy(&adapter->mutex);
kmem_free(adapter->hdlr_argp, sizeof (srv_handler_arg_t));
kmem_free(adapter, sizeof (adapter_t));
}
else
mutex_exit(&adapter->mutex);
DBG_PRINTF((category, RSM_DEBUG_VERBOSE,
"rsmka_release_adapter done\n"));
}
static void
link_adapter(adapter_t *adapter)
{
adapter_listhead_t *listhead;
adapter_t *current;
DBG_PRINTF((category, RSM_DEBUG_VERBOSE, "link_adapter enter\n"));
mutex_enter(&adapter_listhead_base.listlock);
mutex_enter(&adapter->listhead->mutex);
listhead = adapter->listhead;
current = listhead->next_adapter;
listhead->next_adapter = adapter;
adapter->next = current;
ADAPTER_HOLD(adapter);
adapter->listhead->adapter_count++;
mutex_exit(&adapter->listhead->mutex);
mutex_exit(&adapter_listhead_base.listlock);
DBG_PRINTF((category, RSM_DEBUG_VERBOSE, "link_adapter done\n"));
}
static adapter_t *
init_adapter(char *name, int instance, rsm_addr_t hwaddr,
rsm_controller_handle_t handle, rsm_ops_t *ops,
srv_handler_arg_t *hdlr_argp)
{
adapter_t *adapter;
adapter_listhead_t *listhead;
DBG_PRINTF((category, RSM_DEBUG_VERBOSE, "init_adapter enter\n"));
adapter = kmem_zalloc(sizeof (adapter_t), KM_SLEEP);
adapter->instance = instance;
adapter->hwaddr = hwaddr;
adapter->rsmpi_handle = handle;
adapter->rsmpi_ops = ops;
adapter->hdlr_argp = hdlr_argp;
mutex_init(&adapter->mutex, NULL, MUTEX_DEFAULT, NULL);
ADAPTER_HOLD(adapter);
listhead = lookup_adapter_listhead(name);
if (listhead == NULL) {
listhead = init_listhead(name);
}
adapter->listhead = listhead;
link_adapter(adapter);
DBG_PRINTF((category, RSM_DEBUG_VERBOSE, "init_adapter done\n"));
return (adapter);
}
static path_t *
lookup_path(char *adapter_devname, int adapter_instance,
rsm_node_id_t remote_node, rsm_addr_t hwaddr)
{
path_t *current;
adapter_t *adapter;
DBG_PRINTF((category, RSM_DEBUG_VERBOSE, "lookup_path enter\n"));
adapter = rsmka_lookup_adapter(adapter_devname, adapter_instance);
ASSERT(adapter != NULL);
mutex_enter(&adapter->listhead->mutex);
current = adapter->next_path;
while (current != NULL) {
if ((current->remote_node == remote_node) &&
(current->remote_hwaddr == hwaddr) &&
(current->ref_cnt > 0))
break;
else
current = current->next_path;
}
if (current != NULL) {
mutex_enter(¤t->mutex);
PATH_HOLD_NOLOCK(current);
}
mutex_exit(&adapter->listhead->mutex);
ADAPTER_RELE(adapter);
DBG_PRINTF((category, RSM_DEBUG_VERBOSE, "lookup_path done\n"));
return (current);
}
path_t *
rsm_find_path(char *adapter_devname, int adapter_instance, rsm_addr_t hwaddr)
{
path_t *current;
adapter_t *adapter;
DBG_PRINTF((category, RSM_DEBUG_VERBOSE, "rsm_find_path enter\n"));
DBG_PRINTF((category, RSM_DEBUG_VERBOSE,
"rsm_find_path:adapter=%s:%d,rem=%llx\n",
adapter_devname, adapter_instance, hwaddr));
adapter = rsmka_lookup_adapter(adapter_devname, adapter_instance);
if (adapter == NULL)
return (NULL);
mutex_enter(&adapter->listhead->mutex);
current = adapter->next_path;
while (current != NULL) {
if ((current->remote_hwaddr == hwaddr) &&
(current->ref_cnt > 0))
break;
else
current = current->next_path;
}
if (current != NULL) {
mutex_enter(¤t->mutex);
PATH_HOLD_NOLOCK(current);
}
mutex_exit(&adapter->listhead->mutex);
rsmka_release_adapter(adapter);
DBG_PRINTF((category, RSM_DEBUG_VERBOSE, "rsm_find_path done\n"));
return (current);
}
static void
link_path(path_t *path)
{
adapter_t *adapter = path->local_adapter;
path_t *first_path;
DBG_PRINTF((category, RSM_DEBUG_VERBOSE, "link_path enter\n"));
mutex_enter(&adapter_listhead_base.listlock);
mutex_enter(&adapter->listhead->mutex);
first_path = adapter->next_path;
adapter->next_path = path;
path->next_path = first_path;
adapter->listhead->path_count++;
mutex_exit(&adapter->listhead->mutex);
mutex_exit(&adapter_listhead_base.listlock);
DBG_PRINTF((category, RSM_DEBUG_VERBOSE, "link_path done\n"));
}
static void
destroy_path(path_t *path)
{
adapter_t *adapter = path->local_adapter;
path_t *prev, *current;
DBG_PRINTF((category, RSM_DEBUG_VERBOSE, "destroy_path enter\n"));
mutex_enter(&adapter_listhead_base.listlock);
mutex_enter(&path->local_adapter->listhead->mutex);
ASSERT(path->ref_cnt == 0);
prev = NULL;
current = adapter->next_path;
while (current != NULL) {
if (path->remote_node == current->remote_node &&
path->remote_hwaddr == current->remote_hwaddr)
break;
else {
prev = current;
current = current->next_path;
}
}
if (prev == NULL)
adapter->next_path = current->next_path;
else
prev->next_path = current->next_path;
path->local_adapter->listhead->path_count--;
mutex_exit(&path->local_adapter->listhead->mutex);
mutex_exit(&adapter_listhead_base.listlock);
taskq_destroy(path->recv_taskq);
kmem_free(path->msgbuf_queue,
RSMIPC_MAX_MESSAGES * sizeof (msgbuf_elem_t));
mutex_destroy(¤t->mutex);
cv_destroy(¤t->sendq_token.sendq_cv);
cv_destroy(&path->hold_cv);
kmem_free(current, sizeof (path_t));
DBG_PRINTF((category, RSM_DEBUG_VERBOSE, "destroy_path done\n"));
}
void
rsmka_enqueue_msgbuf(path_t *path, void *data)
{
DBG_PRINTF((category, RSM_DEBUG_VERBOSE,
"rsmka_enqueue_msgbuf enter\n"));
ASSERT(MUTEX_HELD(&path->mutex));
ASSERT(path->msgbuf_cnt < RSMIPC_MAX_MESSAGES);
path->msgbuf_cnt++;
if (path->msgbuf_tail == RSMIPC_MAX_MESSAGES - 1) {
path->msgbuf_tail = 0;
} else {
path->msgbuf_tail++;
}
path->msgbuf_queue[path->msgbuf_tail].active = B_TRUE;
bcopy(data, &(path->msgbuf_queue[path->msgbuf_tail].msg),
sizeof (rsmipc_request_t));
DBG_PRINTF((category, RSM_DEBUG_VERBOSE,
"rsmka_enqueue_msgbuf done\n"));
}
void
rsmka_dequeue_msgbuf(path_t *path)
{
DBG_PRINTF((category, RSM_DEBUG_VERBOSE,
"rsmka_dequeue_msgbuf enter\n"));
ASSERT(MUTEX_HELD(&path->mutex));
if (path->msgbuf_cnt == 0)
return;
path->msgbuf_cnt--;
path->msgbuf_queue[path->msgbuf_head].active = B_FALSE;
if (path->msgbuf_head == RSMIPC_MAX_MESSAGES - 1) {
path->msgbuf_head = 0;
} else {
path->msgbuf_head++;
}
DBG_PRINTF((category, RSM_DEBUG_VERBOSE,
"rsmka_dequeue_msgbuf done\n"));
}
msgbuf_elem_t *
rsmka_gethead_msgbuf(path_t *path)
{
msgbuf_elem_t *head;
ASSERT(MUTEX_HELD(&path->mutex));
if (path->msgbuf_cnt == 0)
return (NULL);
head = &path->msgbuf_queue[path->msgbuf_head];
return (head);
}
rsm_node_id_t
get_remote_nodeid(adapter_t *adapter, rsm_addr_t remote_hwaddr)
{
rsm_node_id_t remote_node;
path_t *current = adapter->next_path;
DBG_PRINTF((category, RSM_DEBUG_VERBOSE, "get_remote_nodeid enter\n"));
mutex_enter(&adapter->listhead->mutex);
while (current != NULL) {
if (current->remote_hwaddr == remote_hwaddr) {
remote_node = current->remote_node;
break;
}
current = current->next_path;
}
if (current == NULL)
remote_node = (rsm_node_id_t)-1;
mutex_exit(&adapter->listhead->mutex);
DBG_PRINTF((category, RSM_DEBUG_VERBOSE, "get_remote_nodeid done\n"));
return (remote_node);
}
rsm_addr_t
get_remote_hwaddr(adapter_t *adapter, rsm_node_id_t remote_node)
{
rsm_addr_t remote_hwaddr;
path_t *current = adapter->next_path;
DBG_PRINTF((category, RSM_DEBUG_VERBOSE, "get_remote_hwaddr enter\n"));
mutex_enter(&adapter->listhead->mutex);
while (current != NULL) {
if (current->remote_node == remote_node) {
remote_hwaddr = current->remote_hwaddr;
break;
}
current = current->next_path;
}
if (current == NULL)
remote_hwaddr = -1;
mutex_exit(&adapter->listhead->mutex);
DBG_PRINTF((category, RSM_DEBUG_VERBOSE, "get_remote_hwaddr done\n"));
return (remote_hwaddr);
}
static ipc_info_t *
lookup_ipc_info(rsm_node_id_t remote_node)
{
ipc_info_t *ipc_info;
DBG_PRINTF((category, RSM_DEBUG_VERBOSE, "lookup_ipc_info enter\n"));
mutex_enter(&ipc_info_lock);
ipc_info = ipc_info_head;
if (ipc_info == NULL) {
mutex_exit(&ipc_info_lock);
DBG_PRINTF((category, RSM_DEBUG_VERBOSE,
"lookup_ipc_info done: ipc_info is NULL\n"));
return (NULL);
}
while (ipc_info->remote_node != remote_node) {
ipc_info = ipc_info->next;
if (ipc_info == NULL) {
DBG_PRINTF((category, RSM_DEBUG_VERBOSE,
"lookup_ipc_info: ipc_info not found\n"));
mutex_exit(&ipc_info_lock);
break;
}
}
DBG_PRINTF((category, RSM_DEBUG_VERBOSE, "lookup_ipc_info done\n"));
return (ipc_info);
}
static ipc_info_t *
init_ipc_info(rsm_node_id_t remote_node, boolean_t state)
{
ipc_info_t *ipc_info;
DBG_PRINTF((category, RSM_DEBUG_VERBOSE, "init_ipc_info enter\n"));
ipc_info = kmem_zalloc(sizeof (ipc_info_t), KM_SLEEP);
ipc_info->remote_node = remote_node;
ipc_info->node_is_alive = state;
mutex_enter(&ipc_info_lock);
if (ipc_info_head == NULL) {
DBG_PRINTF((category, RSM_DEBUG,
"init_ipc_info:ipc_info_head = %lx\n", ipc_info));
ipc_info_head = ipc_info;
ipc_info->next = NULL;
} else {
ipc_info->next = ipc_info_head;
ipc_info_head = ipc_info;
}
ipc_info->remote_node = remote_node;
DBG_PRINTF((category, RSM_DEBUG_VERBOSE, "init_ipc_info done\n"));
return (ipc_info);
}
static void
destroy_ipc_info(ipc_info_t *ipc_info)
{
ipc_info_t *current = ipc_info_head;
ipc_info_t *prev;
DBG_PRINTF((category, RSM_DEBUG_VERBOSE, "destroy_ipc_info enter\n"));
ASSERT(MUTEX_HELD(&ipc_info_lock));
while (current != ipc_info) {
prev = current;
current = current->next;
}
ASSERT(current != NULL);
if (current != ipc_info_head)
prev->next = current->next;
else
ipc_info_head = current->next;
kmem_free(current, sizeof (ipc_info_t));
DBG_PRINTF((category, RSM_DEBUG_VERBOSE, "destroy_ipc_info done\n"));
}
static void
link_sendq_token(sendq_token_t *token, rsm_node_id_t remote_node)
{
ipc_info_t *ipc_info;
DBG_PRINTF((category, RSM_DEBUG_VERBOSE, "link_sendq_token enter\n"));
ipc_info = lookup_ipc_info(remote_node);
if (ipc_info == NULL) {
ipc_info = init_ipc_info(remote_node, B_FALSE);
DBG_PRINTF((category, RSM_DEBUG,
"link_sendq_token: new ipc_info = %lx\n", ipc_info));
}
else
DBG_PRINTF((category, RSM_DEBUG,
"link_sendq_token: ipc_info = %lx\n", ipc_info));
if (ipc_info->token_list == NULL) {
ipc_info->token_list = token;
ipc_info->current_token = token;
DBG_PRINTF((category, RSM_DEBUG,
"link_sendq_token: current = %lx\n", token));
token->next = token;
} else {
DBG_PRINTF((category, RSM_DEBUG,
"link_sendq_token: token = %lx\n", token));
token->next = ipc_info->token_list->next;
ipc_info->token_list->next = token;
ipc_info->token_list = token;
}
mutex_exit(&ipc_info_lock);
DBG_PRINTF((category, RSM_DEBUG_VERBOSE, "link_sendq_token done\n"));
}
static void
unlink_sendq_token(sendq_token_t *token, rsm_node_id_t remote_node)
{
sendq_token_t *prev, *start, *current;
ipc_info_t *ipc_info;
path_t *path = SQ_TOKEN_TO_PATH(token);
DBG_PRINTF((category, RSM_DEBUG_VERBOSE,
"unlink_sendq_token enter\n"));
ASSERT(path->ref_cnt == 0);
ipc_info = lookup_ipc_info(remote_node);
if (ipc_info == NULL) {
DBG_PRINTF((category, RSM_DEBUG,
"ipc_info for %d not found\n", remote_node));
return;
}
prev = ipc_info->token_list;
start = current = ipc_info->token_list->next;
for (;;) {
if (current == token) {
if (current->next != current) {
DBG_PRINTF((category, RSM_DEBUG_VERBOSE,
"found token, removed it\n"));
prev->next = token->next;
if (ipc_info->token_list == token)
ipc_info->token_list = prev;
ipc_info->current_token = token->next;
} else {
DBG_PRINTF((category, RSM_DEBUG_VERBOSE,
"removed token, list empty\n"));
ipc_info->token_list = NULL;
ipc_info->current_token = NULL;
}
break;
}
prev = current;
current = current->next;
if (current == start) {
DBG_PRINTF((category, RSM_DEBUG,
"unlink_sendq_token: token not found\n"));
break;
}
}
mutex_exit(&ipc_info_lock);
DBG_PRINTF((category, RSM_DEBUG_VERBOSE, "unlink_sendq_token done\n"));
}
void
rele_sendq_token(sendq_token_t *token)
{
path_t *path;
DBG_PRINTF((category, RSM_DEBUG_VERBOSE, "rele_sendq_token enter\n"));
path = SQ_TOKEN_TO_PATH(token);
mutex_enter(&path->mutex);
PATH_RELE_NOLOCK(path);
SENDQ_TOKEN_RELE(path);
mutex_exit(&path->mutex);
DBG_PRINTF((category, RSM_DEBUG_VERBOSE, "rele_sendq_token done\n"));
}
sendq_token_t *
rsmka_get_sendq_token(rsm_node_id_t remote_node, sendq_token_t *prev_used)
{
sendq_token_t *token, *first_token;
path_t *path;
ipc_info_t *ipc_info;
DBG_PRINTF((category, RSM_DEBUG_VERBOSE,
"rsmka_get_sendq_token enter\n"));
ipc_info = lookup_ipc_info(remote_node);
if (ipc_info == NULL) {
DBG_PRINTF((category, RSM_DEBUG_VERBOSE,
"rsmka_get_sendq_token done: ipc_info is NULL\n"));
return (NULL);
}
if (ipc_info->node_is_alive == B_TRUE) {
token = first_token = ipc_info->current_token;
if (token == NULL) {
mutex_exit(&ipc_info_lock);
DBG_PRINTF((category, RSM_DEBUG_VERBOSE,
"rsmka_get_sendq_token done: token=NULL\n"));
return (NULL);
}
for (;;) {
path = SQ_TOKEN_TO_PATH(token);
DBG_PRINTF((category, RSM_DEBUG_VERBOSE,
"path %lx\n", path));
mutex_enter(&path->mutex);
if (path->state != RSMKA_PATH_ACTIVE ||
path->ref_cnt == 0) {
mutex_exit(&path->mutex);
} else {
if (token != prev_used) {
break;
}
mutex_exit(&path->mutex);
}
token = token->next;
if (token == first_token) {
if (prev_used) {
path = SQ_TOKEN_TO_PATH(prev_used);
DBG_PRINTF((category, RSM_DEBUG_VERBOSE,
"path %lx\n", path));
mutex_enter(&path->mutex);
if (path->state != RSMKA_PATH_ACTIVE ||
path->ref_cnt == 0) {
mutex_exit(&path->mutex);
} else {
token = prev_used;
break;
}
}
mutex_exit(&ipc_info_lock);
DBG_PRINTF((category, RSM_DEBUG_VERBOSE,
"rsmka_get_sendq_token: token=NULL\n"));
return (NULL);
}
}
PATH_HOLD_NOLOCK(path);
SENDQ_TOKEN_HOLD(path);
if (prev_used == NULL) {
ipc_info->current_token = token->next;
}
mutex_exit(&path->mutex);
mutex_exit(&ipc_info_lock);
DBG_PRINTF((category, RSM_DEBUG_VERBOSE,
"rsmka_get_sendq_token done\n"));
return (token);
} else {
mutex_exit(&ipc_info_lock);
DBG_PRINTF((category, RSM_DEBUG_VERBOSE,
"rsmka_get_sendq_token done\n"));
return (NULL);
}
}
static int
create_ipc_sendq(path_t *path)
{
int rval;
sendq_token_t *token;
adapter_t *adapter;
int64_t srvc_offset;
DBG_PRINTF((category, RSM_DEBUG_VERBOSE, "create_ipc_sendq enter\n"));
DBG_PRINTF((category, RSM_DEBUG, "create_ipc_sendq: path = %lx\n",
path));
adapter = path->local_adapter;
token = &path->sendq_token;
srvc_offset = path->remote_hwaddr;
DBG_PRINTF((category, RSM_DEBUG,
"create_ipc_sendq: srvc_offset = %lld\n",
srvc_offset));
rval = adapter->rsmpi_ops->rsm_sendq_create(adapter->rsmpi_handle,
path->remote_hwaddr,
(rsm_intr_service_t)(RSM_SERVICE+srvc_offset),
(rsm_intr_pri_t)RSM_PRI, (size_t)RSM_QUEUE_SZ,
RSM_INTR_SEND_Q_NO_FENCE,
RSM_RESOURCE_SLEEP, NULL, &token->rsmpi_sendq_handle);
if (rval == RSM_SUCCESS) {
mutex_enter(&ipc_info_cvlock);
cv_broadcast(&ipc_info_cv);
mutex_exit(&ipc_info_cvlock);
}
DBG_PRINTF((category, RSM_DEBUG, "create_ipc_sendq: handle = %lx\n",
token->rsmpi_sendq_handle));
DBG_PRINTF((category, RSM_DEBUG, "create_ipc_sendq: rval = %d\n",
rval));
DBG_PRINTF((category, RSM_DEBUG_VERBOSE, "create_ipc_sendq done\n"));
return (rval);
}
boolean_t
rsmka_check_node_alive(rsm_node_id_t remote_node)
{
ipc_info_t *ipc_info;
DBG_PRINTF((category, RSM_DEBUG, "rsmka_check_node_alive enter\n"));
ipc_info = lookup_ipc_info(remote_node);
if (ipc_info == NULL) {
DBG_PRINTF((category, RSM_DEBUG,
"rsmka_check_node_alive done: ipc_info NULL\n"));
return (B_FALSE);
}
if (ipc_info->node_is_alive == B_TRUE) {
mutex_exit(&ipc_info_lock);
DBG_PRINTF((category, RSM_DEBUG,
"rsmka_check_node_alive done: node is alive\n"));
return (B_TRUE);
} else {
mutex_exit(&ipc_info_lock);
DBG_PRINTF((category, RSM_DEBUG,
"rsmka_check_node_alive done: node is not alive\n"));
return (B_FALSE);
}
}
static uint32_t
get_topology_size(int mode)
{
uint32_t topology_size;
int pointer_area_size;
adapter_listhead_t *listhead;
int total_num_of_adapters;
int total_num_of_paths;
DBG_PRINTF((category, RSM_DEBUG_VERBOSE, "get_topology_size enter\n"));
total_num_of_adapters = 0;
total_num_of_paths = 0;
listhead = adapter_listhead_base.next;
while (listhead != NULL) {
total_num_of_adapters += listhead->adapter_count;
total_num_of_paths += listhead->path_count;
listhead = listhead->next_listhead;
}
#ifdef _MULTI_DATAMODEL
if ((mode & DATAMODEL_MASK) == DATAMODEL_ILP32)
pointer_area_size =
(total_num_of_adapters + total_num_of_adapters%2) *
sizeof (caddr32_t);
else
pointer_area_size = total_num_of_adapters * sizeof (caddr_t);
#else
mode = mode;
pointer_area_size = total_num_of_adapters * sizeof (caddr_t);
#endif
topology_size = sizeof (rsmka_topology_hdr_t) +
pointer_area_size +
(total_num_of_adapters * sizeof (rsmka_connections_hdr_t)) +
(total_num_of_paths * sizeof (rsmka_remote_cntlr_t));
DBG_PRINTF((category, RSM_DEBUG_VERBOSE, "get_topology_size done\n"));
return (topology_size);
}
static void
get_topology(caddr_t arg, char *bufp, int mode)
{
rsmka_topology_t *tp = (rsmka_topology_t *)bufp;
adapter_listhead_t *listhead;
adapter_t *adapter;
path_t *path;
int cntlr = 0;
rsmka_connections_t *connection;
rsmka_remote_cntlr_t *rem_cntlr;
int total_num_of_adapters;
#ifdef _MULTI_DATAMODEL
rsmka_topology32_t *tp32 = (rsmka_topology32_t *)bufp;
#else
mode = mode;
#endif
DBG_PRINTF((category, RSM_DEBUG_VERBOSE, "get_topology enter\n"));
total_num_of_adapters = 0;
listhead = adapter_listhead_base.next;
while (listhead != NULL) {
total_num_of_adapters += listhead->adapter_count;
listhead = listhead->next_listhead;
}
tp->topology_hdr.local_nodeid = my_nodeid;
tp->topology_hdr.local_cntlr_count = total_num_of_adapters;
bufp = (char *)&tp->connections[0];
#ifdef _MULTI_DATAMODEL
if ((mode & DATAMODEL_MASK) == DATAMODEL_ILP32)
bufp += (total_num_of_adapters + total_num_of_adapters%2) *
sizeof (caddr32_t);
else
bufp += total_num_of_adapters * sizeof (caddr_t);
#else
bufp += total_num_of_adapters * sizeof (caddr_t);
#endif
listhead = adapter_listhead_base.next;
while (listhead != NULL) {
adapter = listhead->next_adapter;
while (adapter != NULL) {
#ifdef _MULTI_DATAMODEL
if ((mode & DATAMODEL_MASK) == DATAMODEL_ILP32) {
ulong_t delta = (ulong_t)bufp - (ulong_t)tp32;
caddr32_t userbase = (caddr32_t)((ulong_t)arg &
0xffffffff);
tp32->connections[cntlr++] = userbase + delta;
} else {
tp->connections[cntlr++] = arg +
(ulong_t)bufp -
(ulong_t)tp;
}
#else
tp->connections[cntlr++] = arg +
(ulong_t)bufp -
(ulong_t)tp;
#endif
connection = (rsmka_connections_t *)bufp;
(void) snprintf(connection->hdr.cntlr_name,
MAXNAMELEN, "%s%d",
listhead->adapter_devname,
adapter->instance);
connection->hdr.local_hwaddr = adapter->hwaddr;
connection->hdr.remote_cntlr_count = 0;
bufp += sizeof (rsmka_connections_hdr_t);
rem_cntlr = (rsmka_remote_cntlr_t *)bufp;
path = adapter->next_path;
while (path != NULL) {
connection->hdr.remote_cntlr_count++;
rem_cntlr->remote_nodeid = path->remote_node;
(void) snprintf(rem_cntlr->remote_cntlrname,
MAXNAMELEN, "%s%d",
listhead->adapter_devname,
path->remote_devinst);
rem_cntlr->remote_hwaddr = path->remote_hwaddr;
rem_cntlr->connection_state = path->state;
++rem_cntlr;
path = path->next_path;
}
adapter = adapter->next;
bufp = (char *)rem_cntlr;
}
listhead = listhead->next_listhead;
}
DBG_PRINTF((category, RSM_DEBUG_VERBOSE, "get_topology done\n"));
}
int
rsmka_topology_ioctl(caddr_t arg, int cmd, int mode)
{
uint32_t topology_size;
uint32_t request_size;
char *bufp;
int error = RSM_SUCCESS;
size_t max_toposize;
DBG_PRINTF((category | RSM_IOCTL, RSM_DEBUG_VERBOSE,
"rsmka_topology_ioctl enter\n"));
switch (cmd) {
case RSM_IOCTL_TOPOLOGY_SIZE:
mutex_enter(&adapter_listhead_base.listlock);
topology_size = get_topology_size(mode);
mutex_exit(&adapter_listhead_base.listlock);
if (ddi_copyout((caddr_t)&topology_size,
(caddr_t)arg, sizeof (uint32_t), mode))
error = RSMERR_BAD_ADDR;
break;
case RSM_IOCTL_TOPOLOGY_DATA:
if (ddi_copyin(arg, &request_size, sizeof (uint32_t), mode)) {
DBG_PRINTF((category | RSM_IOCTL, RSM_DEBUG_VERBOSE,
"rsmka_topology_ioctl done: BAD_ADDR\n"));
return (RSMERR_BAD_ADDR);
}
max_toposize = sizeof (rsmka_topology_hdr_t) +
RSM_MAX_CTRL * (sizeof (caddr_t) +
sizeof (rsmka_connections_hdr_t)) +
RSM_MAX_NODE * sizeof (rsmka_remote_cntlr_t);
if (request_size > max_toposize) {
DBG_PRINTF((category | RSM_IOCTL, RSM_DEBUG_VERBOSE,
"rsmka_topology_ioctl done: size too large\n"));
return (EINVAL);
}
bufp = kmem_zalloc(request_size, KM_SLEEP);
mutex_enter(&adapter_listhead_base.listlock);
topology_size = get_topology_size(mode);
if (request_size < topology_size) {
kmem_free(bufp, request_size);
mutex_exit(&adapter_listhead_base.listlock);
DBG_PRINTF((category | RSM_IOCTL, RSM_DEBUG_VERBOSE,
"rsmka_topology_ioctl done: E2BIG\n"));
return (E2BIG);
}
get_topology(arg, bufp, mode);
mutex_exit(&adapter_listhead_base.listlock);
if (ddi_copyout((caddr_t)bufp, (caddr_t)arg,
topology_size, mode))
error = RSMERR_BAD_ADDR;
kmem_free(bufp, request_size);
break;
default:
DBG_PRINTF((category | RSM_IOCTL, RSM_DEBUG,
"rsmka_topology_ioctl: cmd not supported\n"));
error = DDI_FAILURE;
}
DBG_PRINTF((category | RSM_IOCTL, RSM_DEBUG_VERBOSE,
"rsmka_topology_ioctl done: %d\n", error));
return (error);
}