/*
 * ocfsgenvote.c
 *
 * IPC based DLM
 *
 * Copyright (C) 2002 Oracle Corporation.  All rights reserved.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 * 
 * You should have recieved a copy of the GNU General Public
 * License along with this program; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 021110-1307, USA.
 *
 * Authors: Neeraj Goyal, Suchit Kaura, Kurt Hackel, Sunil Mushran,
 *          Manish Singh, Wim Coekaerts
 */

#include <ocfs.h>

/* Tracing */
#define  OCFS_DEBUG_CONTEXT  OCFS_DEBUG_CONTEXT_VOTE

/*
 * ocfs_send_vote_reply()
 *
 */
int ocfs_send_vote_reply (ocfs_super * osb, ocfs_dlm_msg * dlm_msg,
			  __u32 vote_status, bool inode_open)
{
	ocfs_dlm_req_master *req_master;
	ocfs_dlm_reply_master *reply_master;
	ocfs_dlm_msg *send_dlm_msg;
	__u64 vote_map;
	int status = 0;
	__u8 *buf = NULL;
	__u32 msg_len;

	LOG_ENTRY ();

	msg_len = sizeof (ocfs_dlm_msg) - 1 + sizeof (ocfs_dlm_reply_master);

	buf = ocfs_malloc (msg_len);
	if (buf == NULL) {
		LOG_ERROR_STATUS (status = -ENOMEM);
		goto finally;
	}

	req_master = (ocfs_dlm_req_master *) dlm_msg->msg_buf;

	send_dlm_msg = (ocfs_dlm_msg *)buf;
	ocfs_init_dlm_msg (osb, send_dlm_msg, msg_len);
	send_dlm_msg->msg_type = OCFS_VOTE_REPLY;

	reply_master = (ocfs_dlm_reply_master *) send_dlm_msg->msg_buf;
	reply_master->h.lock_id = req_master->lock_id;
	reply_master->status = vote_status;
	reply_master->h.lock_seq_num = req_master->lock_seq_num;
	reply_master->h.open_handle = inode_open;

	vote_map = (1 << dlm_msg->src_node);
	ocfs_send_bcast (osb, vote_map, send_dlm_msg);

      finally:
	ocfs_safefree (buf);
	LOG_EXIT_STATUS (status);
	return status;
}				/* ocfs_send_vote_reply */

/*
 * ocfs_comm_vote_for_del_ren()
 *
 */
int ocfs_comm_vote_for_del_ren (ocfs_super * osb, ocfs_lock_res ** lockres,
				ocfs_dlm_msg * dlm_msg)
{
	int status = 0;
	int tmpstat = 0;
	ocfs_dlm_req_master *req_master;
	__u32 node_num;
	__u32 flags;
	__u32 retry_cnt = 0;
	bool acq_oin = false;
	ocfs_file_entry *fe = NULL;
	ocfs_inode *oin = NULL;
	ocfs_sem *oin_sem = NULL;
	struct dentry *dentry;
	struct list_head *iter;
	struct list_head *temp_iter;
	struct inode *inode;
	int refcount;

	LOG_ENTRY ();

	req_master = (ocfs_dlm_req_master *) dlm_msg->msg_buf;

	flags = req_master->flags;
	node_num = dlm_msg->src_node;

	oin = (*lockres)->oin;
	if (oin) {
		ocfs_down_sem (&oin->main_res, true);
		oin->needs_verification = true;
		tmpstat = ocfs_verify_update_oin(osb, oin);
		if (tmpstat < 0)
			LOG_ERROR_STATUS (tmpstat);
		ocfs_up_sem (&oin->main_res);
	}

	LOG_TRACE_ARGS ("node=%u, lockid=%u.%u, seq=%u.%u\n", node_num,
		HILO (req_master->lock_id), HILO (req_master->lock_seq_num));

	/* Check for oin */
	if (oin) {
		oin_sem = &(oin->main_res);
		ocfs_down_sem (oin_sem, true);
		acq_oin = true;

		/* If OIN_IN_USE is set we should go back and retry */
		while ((oin->oin_flags & OCFS_OIN_IN_USE) && (retry_cnt < 5)) {
			if ((acq_oin)) {
				ocfs_up_sem (oin_sem);
				acq_oin = false;
			}

			ocfs_sleep (20);
			retry_cnt++;

			if (!acq_oin) {
				ocfs_down_sem (oin_sem, true);
				acq_oin = true;
			}
		}

		refcount = 0;
		inode = (*lockres)->oin->inode;
		list_for_each_safe (iter, temp_iter, &(inode->i_dentry)) {
				dentry = list_entry (iter, struct dentry, d_alias);
				refcount += atomic_read(&dentry->d_count);
		}

		if (refcount == 0 && (!(oin->oin_flags & OCFS_OIN_IN_USE))) {
			if (!(oin->oin_flags & OCFS_OIN_IN_TEARDOWN)) {
				if (acq_oin) {
					ocfs_up_sem (oin_sem);
					acq_oin = false;
				}

				ocfs_release_lockres (*lockres);

				ocfs_release_cached_oin (osb, oin);
				ocfs_release_oin (oin, true);
				(*lockres) = NULL;
			}
			LOG_TRACE_STR ("vote=FLAG_VOTE_NODE");
			ocfs_send_vote_reply (osb, dlm_msg, FLAG_VOTE_NODE, false);
			goto finito;
		} else {
			LOG_TRACE_STR ("vote=FLAG_VOTE_OIN_ALREADY_INUSE");
			ocfs_send_vote_reply (osb, dlm_msg,
					FLAG_VOTE_OIN_ALREADY_INUSE, false);
			ocfs_release_lockres (*lockres);
			goto finito;
		}
	} else {
		LOG_TRACE_STR ("vote=FLAG_VOTE_NODE");
		ocfs_send_vote_reply (osb, dlm_msg, FLAG_VOTE_NODE, false);
		ocfs_release_lockres (*lockres);
		goto finito;
	}

      finito:
	/* Set the always update master on open flag */
	if (*lockres) {
		(*lockres)->lock_state |= FLAG_ALWAYS_UPDATE_OPEN;
		(*lockres)->last_upd_seq_num = req_master->lock_seq_num;

		if ((*lockres)->master_node_num != OCFS_INVALID_NODE_NUM) {
			if (!IS_NODE_ALIVE (osb->publ_map, (*lockres)->master_node_num,
					    OCFS_MAXIMUM_NODES)) {
				(*lockres)->master_node_num = node_num;
			}
		} else {
			(*lockres)->master_node_num = node_num;
		}

		/* Change the master if there is no lock */
		if (((*lockres)->master_node_num == osb->node_num) &&
		    ((*lockres)->lock_state <= OCFS_DLM_SHARED_LOCK)) {
			__u64 tmp = req_master->lock_id;

			/* Change the lock ownership to the node asking for vote */
			status = ocfs_get_file_entry (osb, &fe, req_master->lock_id);
			if (status < 0) {
				LOG_ERROR_STATUS (status);
				goto finally;
			}

			/* Write new master on the disk */
			DISK_LOCK_CURRENT_MASTER (fe) = node_num;

			status = ocfs_write_disk (osb, fe, osb->sect_size, tmp);
			if (status < 0) {
				LOG_ERROR_STATUS (status);
				goto finally;
			}
			(*lockres)->master_node_num = node_num;
		}
	}

      finally:
	ocfs_release_file_entry (fe);

	if (acq_oin && oin_sem)
		ocfs_up_sem (oin_sem);

	if ((*lockres))
		ocfs_release_lockres (*lockres);

	LOG_EXIT_STATUS (status);
	return status;
}				/* ocfs_comm_vote_for_del_ren */


/*
 * ocfs_check_ipc_msg()
 *
 */
bool ocfs_check_ipc_msg (__u8 * msg, __u32 msg_len)
{
	bool bret = false;
	ocfs_dlm_msg *dlm_msg;

	LOG_ENTRY ();

	dlm_msg = (ocfs_dlm_msg *) msg;

	if (dlm_msg == NULL) {
		LOG_TRACE_STR("Null netdlm message");
		goto bail;
	}

	if (msg_len < sizeof(ocfs_dlm_msg)) {
		LOG_TRACE_STR("Netdlm message too short");
		goto bail;
	}

	/* Compute and Compare the checksum */
	if (dlm_msg->magic != OCFS_DLM_MSG_MAGIC) {
		LOG_TRACE_ARGS ("Magic number mismatch in netdlm message: "
				"0x%08x != 0x%08x\n",
				dlm_msg->magic, OCFS_DLM_MSG_MAGIC);
		goto bail;
	}

	if ((dlm_msg->src_node < 0) ||
	    (dlm_msg->src_node > OCFS_MAXIMUM_NODES)) {
		LOG_TRACE_ARGS ("Invalid source node in netdlm message: %d\n",
				dlm_msg->src_node);
		goto bail;
	}

	bret = true;

      bail:
	LOG_EXIT_LONG (bret);
	return bret;
}				/* ocfs_check_ipc_msg */

/*
 * ocfs_find_osb()
 *
 */
void ocfs_find_osb (__s8 * volume_id, ocfs_super ** osb)
{
	struct list_head *iter_osb;
	struct list_head *temp_iter;

	LOG_ENTRY ();

	ocfs_down_sem (&(OcfsGlobalCtxt.res), true);

	list_for_each_safe (iter_osb, temp_iter, &(OcfsGlobalCtxt.osb_next)) {
		*osb = list_entry (iter_osb, ocfs_super, osb_next);
		if (!memcmp ((*osb)->vol_layout.vol_id, volume_id,
			     MAX_VOL_ID_LENGTH))
			goto bail;
	}

	*osb = NULL;

      bail:
	ocfs_up_sem (&(OcfsGlobalCtxt.res));
	LOG_EXIT ();
	return;
}				/* ocfs_find_osb */

/*
 * ocfs_find_create_lockres()
 *
 */
int ocfs_find_create_lockres (ocfs_super * osb, __u64 lock_id,
			      ocfs_lock_res ** lockres)
{
	int status = 0;
	ocfs_lock_res *tmp_lockres = NULL;

	LOG_ENTRY ();

	*lockres = NULL;

	status = ocfs_lookup_sector_node (osb, lock_id, lockres);
	if (status < 0) {
		*lockres = ocfs_allocate_lockres();
		if (!*lockres) {
			LOG_ERROR_STATUS (status = -ENOMEM);
			goto bail;
		}

		ocfs_init_lockres (osb, *lockres, lock_id);
		ocfs_get_lockres (*lockres);
//		OCFS_SET_FLAG ((*lockres)->vote_state, LOCK_STATE_INIT);

		status = ocfs_insert_sector_node (osb, *lockres, &tmp_lockres);
		if (status < 0) {
			LOG_ERROR_STATUS (status);
			goto bail;
		}

		if (tmp_lockres) {
			ocfs_put_lockres (*lockres);
			*lockres = tmp_lockres;
		}
	}

      bail:
	LOG_EXIT_STATUS (status);
	return status;
}				/* ocfs_find_create_lockres */

/*
 * ocfs_comm_process_vote()
 *
 */
int ocfs_comm_process_vote (ocfs_super * osb, ocfs_dlm_msg * dlm_msg)
{
	int status = 0;
	int tmpstat = 0;
	ocfs_lock_res *lockres = NULL;
	ocfs_dlm_req_master *req_master;
	__u32 node_num = OCFS_INVALID_NODE_NUM;
	__u32 flags;
	__u64 offset;
	__u32 length;
	ocfs_file_entry *fe = NULL;
	ocfs_file_entry *temp_fe = NULL;
	ocfs_inode *oin;
	bool oin_exists;

	LOG_ENTRY ();

	req_master = (ocfs_dlm_req_master *) dlm_msg->msg_buf;

	LOG_TRACE_ARGS("node=%d, lockid=%u.%u, seq=%u.%u\n", dlm_msg->src_node,
		       HI(req_master->lock_id), LO(req_master->lock_id),
		       HI(req_master->lock_seq_num), LO(req_master->lock_seq_num));

	status = ocfs_find_create_lockres (osb, req_master->lock_id, &lockres);
	if (status < 0) {
		if (req_master->flags & FLAG_FILE_UPDATE_OIN) {
			status = ocfs_process_update_inode_request (osb,
					req_master->lock_id, lockres,
					dlm_msg->src_node);
			if (status < 0) {
				LOG_ERROR_STATUS (status);
				goto finally;
			}
			LOG_TRACE_STR ("vote=FLAG_VOTE_OIN_UPDATED");
			ocfs_send_vote_reply (osb, dlm_msg, FLAG_VOTE_OIN_UPDATED, false);
			status = 0;
		} else
			LOG_ERROR_STATUS (status);
		goto finally;
	}

	ocfs_acquire_lockres (lockres);

#if 0
	if (lockres->vote_state & LOCK_STATE_INIT) {
		status = ocfs_send_vote_reply (osb, dlm_msg, FLAG_VOTE_NODE, false);
		goto finally;
	}

	if ((lockres->master_node_num == osb->node_num) &&
	    (dlm_msg->src_node == osb->node_num)) {
		status = ocfs_send_vote_reply (osb, dlm_msg, FLAG_VOTE_NODE, false);
		goto finally;
	}
#endif

	flags = req_master->flags;
	node_num = dlm_msg->src_node;

	if ((flags & FLAG_FILE_DELETE) || (flags & FLAG_FILE_RENAME)) {
		status = ocfs_comm_vote_for_del_ren (osb, &lockres, dlm_msg);
		goto finally;
	}

	if (flags & FLAG_FILE_RELEASE_CACHE) {
		if (!osb->commit_cache_exec) {
			osb->needs_flush = true;
			ocfs_trans_in_progress(osb);

			if (osb->trans_in_progress == false) {
				osb->commit_cache_exec = true;
				ocfs_commit_cache (osb, true);
				osb->needs_flush = false;
				osb->commit_cache_exec = false;
			}

			length = osb->sect_size;
			offset = req_master->lock_id;

			status = ocfs_get_file_entry (osb, &temp_fe,
						      req_master->lock_id);
			if (status < 0) {
				LOG_ERROR_STATUS (status);
				goto finally;
			}

			if (DISK_LOCK_FILE_LOCK (temp_fe) > OCFS_DLM_NO_LOCK) {
				DISK_LOCK_FILE_LOCK (temp_fe) = OCFS_DLM_NO_LOCK;

				status = ocfs_write_force_disk (osb, temp_fe,
								length, offset);
				if (status < 0) {
					LOG_ERROR_STATUS (status);
					goto finally;
				}

				lockres->lock_type = OCFS_DLM_NO_LOCK;
			}

			if (temp_fe) {
				ocfs_release_file_entry (temp_fe);
				temp_fe = NULL;
			}

			LOG_TRACE_STR ("vote=FLAG_VOTE_NODE");
			ocfs_send_vote_reply (osb, dlm_msg, FLAG_VOTE_NODE, false);
			status = 0;
			goto finally;
		}
	}

	if (flags & FLAG_FILE_UPDATE_OIN) {
		if (lockres->oin != NULL) {
			oin = lockres->oin;
			ocfs_down_sem (&(oin->main_res), true);
			oin->needs_verification = true;
			tmpstat = ocfs_verify_update_oin(osb, oin);
			if (tmpstat < 0)
				LOG_ERROR_STATUS (tmpstat);
			ocfs_up_sem (&(oin->main_res));
		} else {
			status = ocfs_process_update_inode_request (osb,
					req_master->lock_id, lockres,
					dlm_msg->src_node);
			if (status < 0) {
				LOG_ERROR_STATUS (status);
				goto finally;
			}
		}
		LOG_TRACE_STR ("vote=FLAG_VOTE_OIN_UPDATED");
		ocfs_send_vote_reply (osb, dlm_msg, FLAG_VOTE_OIN_UPDATED, false);
		status = 0;
		goto finally;
	}

	if (lockres->master_node_num != OCFS_INVALID_NODE_NUM) {
		if (lockres->master_node_num == osb->node_num) {
			if (flags & FLAG_CHANGE_MASTER) {
				__u64 tmp = req_master->lock_id;

				ocfs_commit_cache (osb, true);
				status = ocfs_get_file_entry (osb, &fe,
							      req_master->lock_id);
				if (status < 0) {
					LOG_ERROR_STATUS (status);
					goto finally;
				}

				if (lockres->oin) {
					DISK_LOCK_OIN_MAP (fe) |= (1 << osb->node_num);
				}

				DISK_LOCK_CURRENT_MASTER (fe) = node_num;

				/* Write new master on the disk */
				status = ocfs_write_disk (osb, fe, osb->sect_size, tmp);
				if (status < 0) {
					LOG_ERROR_STATUS (status);
					goto finally;
				}

				lockres->master_node_num = node_num;
				LOG_TRACE_STR ("vote=FLAG_VOTE_NODE");
				ocfs_send_vote_reply (osb, dlm_msg, FLAG_VOTE_NODE, false);
				goto finally;
			} else if (flags & FLAG_ADD_OIN_MAP) {
				__u64 tmp = req_master->lock_id;

				status = ocfs_get_file_entry (osb, &fe, req_master->lock_id);
				if (status < 0) {
					LOG_ERROR_STATUS (status);
					goto finally;
				}

				if ((fe->sync_flags & OCFS_SYNC_FLAG_NAME_DELETED) ||
				    (!(fe-> sync_flags & OCFS_SYNC_FLAG_VALID))) {
					LOG_TRACE_STR ("vote=FLAG_VOTE_FILE_DEL");
					ocfs_send_vote_reply (osb, dlm_msg,
						FLAG_VOTE_FILE_DEL, false);
					goto finally;
				} else {
					DISK_LOCK_OIN_MAP (fe) |= (1 << node_num);

					/* Write new map on the disk */
					status = ocfs_write_disk (osb, fe, osb->sect_size, tmp);
					if (status < 0) {
						LOG_ERROR_STATUS (status);
						goto finally;
					}

					/* Add this node to the oin map on the file entry */
					lockres->oin_openmap = DISK_LOCK_OIN_MAP (fe);
					LOG_TRACE_STR ("vote=FLAG_VOTE_NODE");
					ocfs_send_vote_reply (osb, dlm_msg,
							FLAG_VOTE_NODE, false);
					goto finally;
				}
			}
		} else {
			if (IS_NODE_ALIVE (osb->publ_map, lockres->master_node_num,
					   OCFS_MAXIMUM_NODES)) {
				/* We have no business voting on this lock */
				LOG_TRACE_STR ("vote=FLAG_VOTE_UPDATE_RETRY");
				ocfs_send_vote_reply (osb, dlm_msg,
					FLAG_VOTE_UPDATE_RETRY, false);
			} else {
				oin_exists = false;

				/* Master Node is dead and a vote is needed */
				/* to create a new master */

				if ((lockres->vote_state & LOCK_STATE_IN_VOTING) &&
				    (node_num < osb->node_num)) {
					/* If our node number is > his we win */
					/* so send a mesg to him to retry */
					LOG_TRACE_STR ("vote=FLAG_VOTE_UPDATE_RETRY");
					ocfs_send_vote_reply (osb, dlm_msg,
						FLAG_VOTE_UPDATE_RETRY, oin_exists);
					goto finally;
				} else {
					if ((!(flags & FLAG_DIR)) &&
					    ((flags & FLAG_FILE_EXTEND) ||
					     (flags & FLAG_FILE_UPDATE))) {
						if (lockres->oin) {
							oin_exists = true;
						}
					}
					LOG_TRACE_STR ("vote=FLAG_VOTE_NODE");
					ocfs_send_vote_reply (osb, dlm_msg,
						FLAG_VOTE_NODE, oin_exists);
				}
			}
		}
	} else {
		oin_exists = false;

		if ((lockres->vote_state & LOCK_STATE_IN_VOTING) &&
		    (node_num < osb->node_num)) {
			/* If our node number is > his we win so send a mesg */
			/* to him to retry */
			LOG_TRACE_STR ("vote=FLAG_VOTE_UPDATE_RETRY");
			ocfs_send_vote_reply (osb, dlm_msg,
					FLAG_VOTE_UPDATE_RETRY, oin_exists);
			goto finally;
		} else {
			/* Vote for the node */
			if ((!(flags & FLAG_DIR)) &&
			    ((flags & FLAG_FILE_EXTEND) || (flags & FLAG_FILE_UPDATE))) {
				if (lockres->oin) {
					oin_exists = true;
				}
			}

			LOG_TRACE_STR ("vote=FLAG_VOTE_NODE");
			ocfs_send_vote_reply (osb, dlm_msg, FLAG_VOTE_NODE, oin_exists);
			goto finally;
		}
	}

      finally:
	ocfs_release_file_entry (fe);
	if (lockres)
		ocfs_release_lockres (lockres);
	ocfs_put_lockres (lockres);

	LOG_EXIT_STATUS (status);
	return status;
}				/* ocfs_comm_process_vote */

/*
 * ocfs_comm_process_vote_reply()
 *
 */
int ocfs_comm_process_vote_reply (ocfs_super * osb, ocfs_dlm_msg * dlm_msg)
{
	int status = 0;
	ocfs_lock_res *lockres = NULL;
	ocfs_dlm_reply_master *reply;
	ocfs_dlm_msg_hdr *reply_msg;

	LOG_ENTRY ();

	down (&(osb->comm_lock));

	reply = (ocfs_dlm_reply_master *) dlm_msg->msg_buf;
	reply_msg = &(reply->h);

	status = ocfs_lookup_sector_node (osb, reply_msg->lock_id, &lockres);
	if (status < 0) {
		LOG_TRACE_ARGS ("status=%d, id=%u.%u\n", status,
			       	HILO(reply_msg->lock_id));
		goto bail;
	}

	if (!(lockres->vote_state & LOCK_STATE_IN_VOTING) ||
	    (lockres->last_upd_seq_num != reply_msg->lock_seq_num)) {
		LOG_TRACE_STR ("Ignoring netdlm message");
		goto bail;
	}

	LOG_TRACE_ARGS("node=%u, lockid=%u.%u, seq=%u.%u, vote=%d\n",
		       dlm_msg->src_node, HI(reply_msg->lock_id),
		       LO(reply_msg->lock_id), HI(reply_msg->lock_seq_num),
		       LO(reply_msg->lock_seq_num), reply->status);

	switch (reply->status) {
	case FLAG_VOTE_NODE:
		lockres->vote_status = 0;
		lockres->got_vote_map |= (1 << dlm_msg->src_node);

		if ((reply_msg->flags & FLAG_FILE_EXTEND ||
		     reply_msg->flags & FLAG_FILE_UPDATE) && reply_msg->open_handle)
			lockres->tmp_openmap |=
				(reply_msg->open_handle << dlm_msg->src_node);
		break;

	case FLAG_VOTE_OIN_ALREADY_INUSE:
		lockres->got_vote_map |= (1 << dlm_msg->src_node);
		lockres->vote_status = -EFAIL;
		if (reply_msg->flags & FLAG_FILE_DELETE)
			lockres->vote_status = -EBUSY;
		break;

	case FLAG_VOTE_OIN_UPDATED:
		lockres->got_vote_map |= (1 << dlm_msg->src_node);
		break;

	case FLAG_VOTE_UPDATE_RETRY:
		lockres->vote_status = -EAGAIN;
		break;

	case FLAG_VOTE_FILE_DEL:
		lockres->vote_status = -ENOENT;
		break;
	}

	if (lockres->vote_status != 0) {
		lockres->vote_state = 0;
		atomic_set (&lockres->voted_event_woken, 1);
		wake_up (&lockres->voted_event);
		goto bail;
	}

	if (lockres->got_vote_map == lockres->req_vote_map) {
		if ((reply_msg->flags & FLAG_FILE_EXTEND) ||
		    (reply_msg->flags & FLAG_FILE_UPDATE))
			lockres->oin_openmap = lockres->tmp_openmap;
		lockres->tmp_openmap = 0;
		LOG_TRACE_ARGS ("OK vote, lockid=%u.%u, map=0x%08x\n",
				HI(lockres->sector_num), LO(lockres->sector_num),
				LO(lockres->got_vote_map));
		lockres->vote_state = 0;
		atomic_set (&lockres->voted_event_woken, 1);
		wake_up (&lockres->voted_event);
	}

      bail:
	ocfs_put_lockres (lockres);
	up (&(osb->comm_lock));
	LOG_EXIT_STATUS (status);
	return status;
}				/* ocfs_comm_process_vote_reply */

/*
 * ocfs_dlm_recv_msg()
 *
 */
void ocfs_dlm_recv_msg (void *val)
{
	ocfs_recv_ctxt *recv_ctxt;
	__u8 *dlm_packet;

	LOG_ENTRY ();

	recv_ctxt = (ocfs_recv_ctxt *) val;
	dlm_packet = (__u8 *) recv_ctxt->msg;

	if (recv_ctxt->status >= 0) {
		if (ocfs_check_ipc_msg (dlm_packet, recv_ctxt->msg_len))
			ocfs_comm_process_msg (dlm_packet);
	}

	ocfs_safefree (recv_ctxt);

	LOG_EXIT ();
	return;
}				/* ocfs_dlm_recv_msg */

/*
 * ocfs_comm_process_msg()
 *
 */
int ocfs_comm_process_msg (__u8 * msg)
{
	int status = 0;
	ocfs_super *osb;
	ocfs_dlm_msg *dlm_msg;
	__u64 nodemap;

	LOG_ENTRY ();

	dlm_msg = (ocfs_dlm_msg *) msg;

	ocfs_find_osb (dlm_msg->vol_id, &osb);
	if (osb == NULL) {
		LOG_TRACE_STR("Ignoring netdlm message with invalid volume id");
		goto bail;
	}

	nodemap = (1 << dlm_msg->src_node);
	if (!(osb->publ_map & nodemap)) {
		LOG_TRACE_STR("Ignoring netdlm message from dead node");
		goto bail;
	}

	switch (dlm_msg->msg_type) {
	case OCFS_VOTE_REQUEST:
		ocfs_comm_process_vote (osb, dlm_msg);
		break;

	case OCFS_VOTE_REPLY:
		ocfs_comm_process_vote_reply (osb, dlm_msg);
		break;
	
	case OCFS_INFO_DISMOUNT:
		ocfs_comm_process_dismount (osb, dlm_msg);
		break;

	default:
		break;
	}

      bail:
	LOG_EXIT_STATUS (status);
	return status;
}				/* ocfs_comm_process_msg */


/*
 * ocfs_send_dismount_msg()
 *
 */
int ocfs_send_dismount_msg (ocfs_super * osb, __u64 vote_map)
{
	int status = 0;
	ocfs_dlm_msg *dlm_msg = NULL;
	__u32 msg_len;
	ocfs_dlm_msg_hdr *req;

	LOG_ENTRY_ARGS ("(osb=0x%08x, vm=0x%08x)\n", osb, LO(vote_map));

	msg_len = sizeof (ocfs_dlm_msg) - 1 + sizeof (ocfs_dlm_req_master);

	dlm_msg = ocfs_malloc (msg_len);
	if (dlm_msg == NULL) {
		LOG_ERROR_STATUS (status = -ENOMEM);
		goto finally;
	}

	ocfs_init_dlm_msg (osb, dlm_msg, msg_len);

	dlm_msg->msg_type = OCFS_INFO_DISMOUNT;

	req = (ocfs_dlm_msg_hdr *) dlm_msg->msg_buf;
	req->lock_id = 0;
	req->flags = 0;
	req->lock_seq_num = 0;
	req->open_handle = 0;

	ocfs_send_bcast (osb, vote_map, dlm_msg);

      finally:
	ocfs_safefree (dlm_msg);
	LOG_EXIT_STATUS (status);
	return status;
}				/* ocfs_send_dismount_msg */


/*
 * ocfs_comm_process_dismount()
 *
 */
void ocfs_comm_process_dismount (ocfs_super * osb, ocfs_dlm_msg * dlm_msg)
{
	ocfs_dlm_req_master *req_master;

	LOG_ENTRY ();

	req_master = (ocfs_dlm_req_master *) dlm_msg->msg_buf;

	LOG_TRACE_ARGS("node=%d", dlm_msg->src_node);
	printk ("ocfs: Received dismount message from node %d\n",
		dlm_msg->src_node);

	atomic_set (&(osb->vol_node_map.dismount[dlm_msg->src_node]), 1);

	LOG_EXIT ();
	return ;
}				/* ocfs_comm_process_dismount */
