Blame |
Last modification |
View Log
| RSS feed
/*
* include/linux/sunrpc/cache.h
*
* Generic code for various authentication-related caches
* used by sunrpc clients and servers.
*
* Copyright (C) 2002 Neil Brown <neilb@cse.unsw.edu.au>
*
* Released under terms in GPL version 2. See COPYING.
*
*/
#ifndef _LINUX_SUNRPC_CACHE_H_
#define _LINUX_SUNRPC_CACHE_H_
#include <linux/slab.h>
#include <asm/atomic.h>
#include <linux/proc_fs.h>
/*
* Each cache requires:
* - A 'struct cache_detail' which contains information specific to the cache
* for common code to use.
* - An item structure that must contain a "struct cache_head"
* - A lookup function defined using DefineCacheLookup
* - A 'put' function that can release a cache item. It will only
* be called after cache_put has succeed, so there are guarantee
* to be no references.
* - A function to calculate a hash of an item's key.
*
* as well as assorted code fragments (e.g. compare keys) and numbers
* (e.g. hash size, goal_age, etc).
*
* Each cache must be registered so that it can be cleaned regularly.
* When the cache is unregistered, it is flushed completely.
*
* Entries have a ref count and a 'hashed' flag which counts the existance
* in the hash table.
* We only expire entries when refcount is zero.
* Existance in the cache is not measured in refcount but rather in
* CACHE_HASHED flag.
*/
/* Every cache item has a common header that is used
* for expiring and refreshing entries.
*
*/
struct cache_head {
struct cache_head * next;
time_t expiry_time; /* After time time, don't use the data */
time_t last_refresh; /* If CACHE_PENDING, this is when upcall
* was sent, else this is when update was received
*/
atomic_t refcnt;
unsigned long flags;
};
#define CACHE_VALID 0 /* Entry contains valid data */
#define CACHE_NEGATIVE 1 /* Negative entry - there is no match for the key */
#define CACHE_PENDING 2 /* An upcall has been sent but no reply received yet*/
#define CACHE_HASHED 3 /* Entry is in a hash table */
#define CACHE_NEW_EXPIRY 120 /* keep new things pending confirmation for 120 seconds */
struct cache_detail {
int hash_size;
struct cache_head ** hash_table;
rwlock_t hash_lock;
atomic_t inuse; /* active user-space update or lookup */
char *name;
void (*cache_put)(struct cache_head *,
struct cache_detail*);
void (*cache_request)(struct cache_detail *cd,
struct cache_head *h,
char **bpp, int *blen);
int (*cache_parse)(struct cache_detail *,
char *buf, int len);
int (*cache_show)(struct seq_file *m,
struct cache_detail *cd,
struct cache_head *h);
/* fields below this comment are for internal use
* and should not be touched by cache owners
*/
time_t flush_time; /* flush all cache items with last_refresh
* earlier than this */
struct list_head others;
time_t nextcheck;
int entries;
/* fields for communication over channel */
struct list_head queue;
struct proc_dir_entry *proc_ent;
atomic_t readers; /* how many time is /chennel open */
time_t last_close; /* it no readers, when did last close */
};
/* this must be embedded in any request structure that
* identifies an object that will want a callback on
* a cache fill
*/
struct cache_req {
struct cache_deferred_req *(*defer)(struct cache_req *req);
};
/* this must be embedded in a deferred_request that is being
* delayed awaiting cache-fill
*/
struct cache_deferred_req {
struct list_head hash; /* on hash chain */
struct list_head recent; /* on fifo */
struct cache_head *item; /* cache item we wait on */
time_t recv_time;
void *owner; /* we might need to discard all defered requests
* owned by someone */
void (*revisit)(struct cache_deferred_req *req,
int too_many);
};
/*
* just like a template in C++, this macro does cache lookup
* for us.
* The function is passed some sort of HANDLE from which a cache_detail
* structure can be determined (via SETUP, DETAIL), a template
* cache entry (type RTN*), and a "set" flag. Using the HASHFN and the
* TEST, the function will try to find a matching cache entry in the cache.
* If "set" == 0 :
* If an entry is found, it is returned
* If no entry is found, a new non-VALID entry is created.
* If "set" == 1 :
* If no entry is found a new one is inserted with data from "template"
* If a non-CACHE_VALID entry is found, it is updated from template using UPDATE
* If a CACHE_VALID entry is found, a new entry is swapped in with data
* from "template"
* If set == 2, we UPDATE, but don't swap. i.e. update in place
*
* If the passed handle has the CACHE_NEGATIVE flag set, then UPDATE is not
* run but insteead CACHE_NEGATIVE is set in any new item.
* In any case, the new entry is returned with a reference count.
*
*
* RTN is a struct type for a cache entry
* MEMBER is the member of the cache which is cache_head, which must be first
* FNAME is the name for the function
* ARGS are arguments to function and must contain RTN *item, int set. May
* also contain something to be usedby SETUP or DETAIL to find cache_detail.
* SETUP locates the cache detail and makes it available as...
* DETAIL identifies the cache detail, possibly set up by SETUP
* HASHFN returns a hash value of the cache entry "item"
* TEST tests if "tmp" matches "item"
* INIT copies key information from "item" to "new"
* UPDATE copies content information from "item" to "tmp"
* INPLACE is true if updates can happen inplace rather than allocating a new structure
*/
#define DefineCacheLookup(RTN,MEMBER,FNAME,ARGS,SETUP,DETAIL,HASHFN,TEST,INIT,UPDATE,INPLACE) \
RTN *FNAME ARGS \
{ \
RTN *tmp, *new=NULL; \
struct cache_head **hp, **head; \
SETUP; \
retry: \
head = &(DETAIL)->hash_table[HASHFN]; \
if (set||new) write_lock(&(DETAIL)->hash_lock); \
else read_lock(&(DETAIL)->hash_lock); \
for(hp=head; *hp != NULL; hp = &tmp->MEMBER.next) { \
tmp = container_of(*hp, RTN, MEMBER); \
if (TEST) { /* found a match */ \
\
if (set && !INPLACE && test_bit(CACHE_VALID, &tmp->MEMBER.flags) && !new) \
break; \
\
cache_get(&tmp->MEMBER); \
if (set) { \
if (!INPLACE && test_bit(CACHE_VALID, &tmp->MEMBER.flags))\
{ /* need to swap in new */ \
RTN *t2; \
\
new->MEMBER.next = tmp->MEMBER.next; \
*hp = &new->MEMBER; \
tmp->MEMBER.next = NULL; \
set_bit(CACHE_HASHED, &new->MEMBER.flags); \
clear_bit(CACHE_HASHED, &tmp->MEMBER.flags); \
t2 = tmp; tmp = new; new = t2; \
} \
if (test_bit(CACHE_NEGATIVE, &item->MEMBER.flags)) \
set_bit(CACHE_NEGATIVE, &tmp->MEMBER.flags); \
else {UPDATE;} \
} \
if (set||new) write_unlock(&(DETAIL)->hash_lock); \
else read_unlock(&(DETAIL)->hash_lock); \
if (set) \
cache_fresh(DETAIL, &tmp->MEMBER, item->MEMBER.expiry_time); \
if (set && !INPLACE && new) cache_fresh(DETAIL, &new->MEMBER, 0); \
if (new) (DETAIL)->cache_put(&new->MEMBER, DETAIL); \
return tmp; \
} \
} \
/* Didn't find anything */ \
if (new) { \
new->MEMBER.next = *head; \
*head = &new->MEMBER; \
(DETAIL)->entries ++; \
set_bit(CACHE_HASHED, &new->MEMBER.flags); \
if (set) { \
tmp = new; \
if (test_bit(CACHE_NEGATIVE, &item->MEMBER.flags)) \
set_bit(CACHE_NEGATIVE, &tmp->MEMBER.flags); \
else {UPDATE;} \
} \
} \
if (set||new) write_unlock(&(DETAIL)->hash_lock); \
else read_unlock(&(DETAIL)->hash_lock); \
if (new && set) \
cache_fresh(DETAIL, &new->MEMBER, item->MEMBER.expiry_time); \
if (new) \
return new; \
new = kmalloc(sizeof(*new), GFP_KERNEL); \
if (new) { \
cache_init(&new->MEMBER); \
cache_get(&new->MEMBER); \
INIT; \
tmp = new; \
goto retry; \
} \
return NULL; \
}
#define DefineSimpleCacheLookup(STRUCT,INPLACE) \
DefineCacheLookup(struct STRUCT, h, STRUCT##_lookup, (struct STRUCT *item, int set), /*no setup */, \
& STRUCT##_cache, STRUCT##_hash(item), STRUCT##_match(item, tmp),\
STRUCT##_init(new, item), STRUCT##_update(tmp, item),INPLACE)
#define cache_for_each(pos, detail, index, member) \
for (({read_lock(&(detail)->hash_lock); index = (detail)->hash_size;}) ; \
({if (index==0)read_unlock(&(detail)->hash_lock); index--;}); \
) \
for (pos = container_of((detail)->hash_table[index], typeof(*pos), member); \
&pos->member; \
pos = container_of(pos->member.next, typeof(*pos), member))
extern void cache_defer_req(struct cache_req *req, struct cache_head *item);
extern void cache_revisit_request(struct cache_head *item);
extern void cache_clean_deferred(void *owner);
static inline struct cache_head *cache_get(struct cache_head *h)
{
atomic_inc(&h->refcnt);
return h;
}
static inline int cache_put(struct cache_head *h, struct cache_detail *cd)
{
atomic_dec(&h->refcnt);
if (!atomic_read(&h->refcnt) &&
h->expiry_time < cd->nextcheck)
cd->nextcheck = h->expiry_time;
if (!test_bit(CACHE_HASHED, &h->flags) &&
!atomic_read(&h->refcnt))
return 1;
return 0;
}
extern void cache_init(struct cache_head *h);
extern void cache_fresh(struct cache_detail *detail,
struct cache_head *head, time_t expiry);
extern int cache_check(struct cache_detail *detail,
struct cache_head *h, struct cache_req *rqstp);
extern int cache_clean(void);
extern void cache_flush(void);
extern void cache_purge(struct cache_detail *detail);
#define NEVER (0x7FFFFFFF)
extern void cache_register(struct cache_detail *cd);
extern int cache_unregister(struct cache_detail *cd);
extern struct cache_detail *cache_find(char *name);
extern void cache_drop(struct cache_detail *detail);
extern void qword_add(char **bpp, int *lp, char *str);
extern void qword_addhex(char **bpp, int *lp, char *buf, int blen);
extern int qword_get(char **bpp, char *dest, int bufsize);
static inline int get_int(char **bpp, int *anint)
{
char buf[50];
char *ep;
int rv;
int len = qword_get(bpp, buf, 50);
if (len < 0) return -EINVAL;
if (len ==0) return -ENOENT;
rv = simple_strtol(buf, &ep, 0);
if (*ep) return -EINVAL;
*anint = rv;
return 0;
}
static inline time_t get_expiry(char **bpp)
{
int rv;
if (get_int(bpp, &rv))
return 0;
if (rv < 0)
return 0;
return rv;
}
#endif /* _LINUX_SUNRPC_CACHE_H_ */