/*
 * Copyright (c) 2006 Silicon Graphics, Inc.
 * 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.
 *
 * This program is distributed in the hope that it would be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write the Free Software Foundation,
 * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */
#ifndef __CACHE_H__
#define __CACHE_H__

/*
 * initialisation flags
 */
/*
 * xfs_db always writes changes immediately, and so we need to purge buffers
 * when we get a buffer lookup mismatch due to reading the same block with a
 * different buffer configuration.
 */
#define CACHE_MISCOMPARE_PURGE	(1 << 0)

/*
 * cache object campare return values
 */
enum {
	CACHE_HIT,
	CACHE_MISS,
	CACHE_PURGE,
};

#define	HASH_CACHE_RATIO	8

/*
 * Cache priorities range from BASE to MAX.
 *
 * For prefetch support, the top half of the range starts at
 * CACHE_PREFETCH_PRIORITY and everytime the buffer is fetched and is at or
 * above this priority level, it is reduced to below this level (refer to
 * libxfs_getbuf).
 *
 * If we have dirty nodes, we can't recycle them until they've been cleaned. To
 * keep these out of the reclaimable lists (as there can be lots of them) give
 * them their own priority that the shaker doesn't attempt to walk.
 */

#define CACHE_BASE_PRIORITY	0
#define CACHE_PREFETCH_PRIORITY	8
#define CACHE_MAX_PRIORITY	15
#define CACHE_DIRTY_PRIORITY	(CACHE_MAX_PRIORITY + 1)
#define CACHE_NR_PRIORITIES	CACHE_DIRTY_PRIORITY

/*
 * Simple, generic implementation of a cache (arbitrary data).
 * Provides a hash table with a capped number of cache entries.
 */

struct cache;
struct cache_node;

typedef void *cache_key_t;

typedef void (*cache_walk_t)(struct cache_node *);
typedef struct cache_node * (*cache_node_alloc_t)(cache_key_t);
typedef int (*cache_node_flush_t)(struct cache_node *);
typedef void (*cache_node_relse_t)(struct cache_node *);
typedef unsigned int (*cache_node_hash_t)(cache_key_t, unsigned int,
					  unsigned int);
typedef int (*cache_node_compare_t)(struct cache_node *, cache_key_t);
typedef unsigned int (*cache_bulk_relse_t)(struct cache *, struct list_head *);

struct cache_operations {
	cache_node_hash_t	hash;
	cache_node_alloc_t	alloc;
	cache_node_flush_t	flush;
	cache_node_relse_t	relse;
	cache_node_compare_t	compare;
	cache_bulk_relse_t	bulkrelse;	/* optional */
};

struct cache_hash {
	struct list_head	ch_list;	/* hash chain head */
	unsigned int		ch_count;	/* hash chain length */
	pthread_mutex_t		ch_mutex;	/* hash chain mutex */
};

struct cache_mru {
	struct list_head	cm_list;	/* MRU head */
	unsigned int		cm_count;	/* MRU length */
	pthread_mutex_t		cm_mutex;	/* MRU lock */
};

struct cache_node {
	struct list_head	cn_hash;	/* hash chain */
	struct list_head	cn_mru;		/* MRU chain */
	unsigned int		cn_count;	/* reference count */
	unsigned int		cn_hashidx;	/* hash chain index */
	int			cn_priority;	/* priority, -1 = free list */
	int			cn_old_priority;/* saved pre-dirty prio */
	pthread_mutex_t		cn_mutex;	/* node mutex */
};

struct cache {
	int			c_flags;	/* behavioural flags */
	unsigned int		c_maxcount;	/* max cache nodes */
	unsigned int		c_count;	/* count of nodes */
	pthread_mutex_t		c_mutex;	/* node count mutex */
	cache_node_hash_t	hash;		/* node hash function */
	cache_node_alloc_t	alloc;		/* allocation function */
	cache_node_flush_t	flush;		/* flush dirty data function */
	cache_node_relse_t	relse;		/* memory free function */
	cache_node_compare_t	compare;	/* comparison routine */
	cache_bulk_relse_t	bulkrelse;	/* bulk release routine */
	unsigned int		c_hashsize;	/* hash bucket count */
	unsigned int		c_hashshift;	/* hash key shift */
	struct cache_hash	*c_hash;	/* hash table buckets */
	struct cache_mru	c_mrus[CACHE_DIRTY_PRIORITY + 1];
	unsigned long long	c_misses;	/* cache misses */
	unsigned long long	c_hits;		/* cache hits */
	unsigned int 		c_max;		/* max nodes ever used */
};

struct cache *cache_init(int, unsigned int, struct cache_operations *);
void cache_destroy(struct cache *);
void cache_walk(struct cache *, cache_walk_t);
void cache_purge(struct cache *);
void cache_flush(struct cache *);

int cache_node_get(struct cache *, cache_key_t, struct cache_node **);
void cache_node_put(struct cache *, struct cache_node *);
void cache_node_set_priority(struct cache *, struct cache_node *, int);
int cache_node_get_priority(struct cache_node *);
int cache_node_purge(struct cache *, cache_key_t, struct cache_node *);
void cache_report(FILE *fp, const char *, struct cache *);
int cache_overflowed(struct cache *);

#endif	/* __CACHE_H__ */
