/*
 * Copyright (c) 2006,2014-2018 The DragonFly Project.  All rights reserved.
 *
 * This code is derived from software contributed to The DragonFly Project
 * by Matthew Dillon <dillon@backplane.com>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 * 3. Neither the name of The DragonFly Project nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific, prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

/*
 * CCMS - Cache Coherency Management System.
 *
 * This subsystem can be tied into a VFS in order to supply persistent
 * cache management state for cluster or for remote cache-coherent operations.
 *
 * Local and cluster/remote cache state is maintained in a cache-coherent
 * fashion as well as integrated into the VFS's inode locking subsystem
 * (as a means of avoiding deadlocks).
 *
 * To operate properly the VFS must maintain a complete directory topology
 * leading to any given vnode/inode either open or cached by the system.
 * The vnode/namecache subsystem does not have to implement this but the
 * VFS (aka HAMMER2) does.
 *
 * The filesystem embeds CCMS_CST structures in its internal inode
 * representatino as needed and implements callback to allow CCMS to
 * do topological recursions.
 *
 * --
 *
 * The CCMS_CST structures represent granted cache and local locking states.
 * Grants can be recursively inherited, minimizing protocol overhead in
 * situations where there are no conflicts of interest.
 *
 * --
 *
 * CCMS supports active front-end 'locks' on data objects utilizing the
 * ccms_inode, key, and desired cache state.  It can grant the lock based
 * on inherited CST state and prevents downgrading of the CST by other
 * parties or threads while the lock is held.  The CST's arranged
 * lock within the embedded CCMS_INODE and ref-counts the related CST.
 */

#ifndef _SYS_CCMS_H_
#define _SYS_CCMS_H_

#ifndef _SYS_TYPES_H_
#include <sys/types.h>
#endif
#ifndef _SYS_PARAM_H_
#include <sys/param.h>
#endif
#ifndef _SYS_SERIALIZE_H_
#include <sys/serialize.h>
#endif
#ifndef _SYS_SPINLOCK_H_
#include <sys/spinlock.h>
#endif

typedef uint64_t	ccms_key_t;
typedef uint64_t	ccms_tid_t;
typedef uint8_t		ccms_state_t;
typedef uint8_t		ccms_type_t;

struct ccms_cst;

/*
 * CCMS_STATE_T - CCMS cache states.
 *
 * INVALID   -	Cache state is unknown and must be acquired.
 *
 * ALLOWED   -  Cache state allows any recursive state to be acquired.
 *
 * SHARED    -	Cache state allows shared access.
 *
 * EXCLUSIVE -  Cache state allows exclusive access.
 *
 * CCMS Implements an extended MESI model.  The extensions are implemented
 * as CCMS_TYPE_T flags.
 */
#define CCMS_STATE_INVALID	0	/* unknown cache state */
#define CCMS_STATE_SHARED	2	/* clean, shared, read-only */
#define CCMS_STATE_EXCLUSIVE	3	/* clean, exclusive, read-only */

/*
 * CCMS_TYPE_T FLAGS
 *
 * INHERITED -  Indicates the state field was inherited and was not directly
 *		granted by the cluster controller.
 *
 * MODIFIED  -  This is a type-field flag associated with an EXCLUSIVE cache
 *		state
 *
 * MASTER    -  This is a type-field flag associated with an EXCLUSIVE+MODIFIED
 *		cache state which indicates that slaves might be present
 *		which are caching our unsynchronized state.
 *
 * SLAVE     -  This is a type-field flag associated with the SHARED cache
 *		state which indicates that the data present in our memory
 *		caches is being mastered elsewhere and has not been
 *		synchronized (meaning no quorum protocol has been run to
 *		sync the data yet).  Thus only the version of the data in
 *		our memory and its originator is valid.
 *
 * QSLAVE    -  This indicates that the slaved data is also present in the
 *		memory caches of a quorum of master nodes.
 */
#define CCMS_TYPE_INHERITED	0x01
#define CCMS_TYPE_MODIFIED	0x02
#define CCMS_TYPE_MASTER	0x04
#define CCMS_TYPE_SLAVE		0x08
#define CCMS_TYPE_QSALVE	0x10
#define CCMS_TYPE_RECURSIVE	0x80

/*
 * CCMS_CST - Low level locking state, persistent cache state
 *
 * Offset ranges are byte-inclusive, allowing the entire 64 bit data space
 * to be represented without overflowing the edge case.  For example, a
 * 64 byte area might be represented as (0,63).  The offsets are UNSIGNED
 * entities.
 *
 * High level CST locks must be obtained top-down.
 *
 * count - Negative value indicates active exclusive lock, positive value
 *	   indicates active shared lock.
 *
 * spin  - Structural spinlock, typically just one is held at a time.
 *	   However, to complement the top-down nature of the higher level
 *	   lock we allow the spin lock to be held recursively in a bottom-up
 *	   fashion for race-to-root flags updates and lastdrop iterations.
 */
struct ccms_cst {
	hammer2_spin_t	spin;		/* thread spinlock */
	ccms_state_t	state;		/* granted or inherited state */
	ccms_type_t	type;		/* CST type and flags */
	uint8_t		unused02;
	uint8_t		unused03;

	int32_t		upgrade;	/* upgrades pending */
	int32_t		count;		/* active shared/exclusive count */
	int32_t		blocked;	/* wakeup blocked on release */
	thread_t	td;		/* if excl lock (count < 0) */
};

typedef struct ccms_cst		ccms_cst_t;

/*
 * Kernel API
 */
#ifdef _KERNEL

void ccms_cst_init(ccms_cst_t *cst);
void ccms_cst_uninit(ccms_cst_t *cst);

void ccms_thread_lock(ccms_cst_t *cst, ccms_state_t state);
int ccms_thread_lock_nonblock(ccms_cst_t *cst, ccms_state_t state);
ccms_state_t ccms_thread_lock_temp_release(ccms_cst_t *cst);
void ccms_thread_lock_temp_restore(ccms_cst_t *cst, ccms_state_t ostate);
ccms_state_t ccms_thread_lock_upgrade(ccms_cst_t *cst);
void ccms_thread_lock_downgrade(ccms_cst_t *cst, ccms_state_t ostate);
void ccms_thread_unlock(ccms_cst_t *cst);
void ccms_thread_unlock_upgraded(ccms_cst_t *cst, ccms_state_t ostate);
int ccms_thread_lock_owned(ccms_cst_t *cst);
void ccms_thread_lock_setown(ccms_cst_t *cst);

#endif

#endif
