Subversion Repositories shark

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
423 giacomo 1
/*
2
 * include/linux/sunrpc/cache.h
3
 *
4
 * Generic code for various authentication-related caches
5
 * used by sunrpc clients and servers.
6
 *
7
 * Copyright (C) 2002 Neil Brown <neilb@cse.unsw.edu.au>
8
 *
9
 * Released under terms in GPL version 2.  See COPYING.
10
 *
11
 */
12
 
13
#ifndef _LINUX_SUNRPC_CACHE_H_
14
#define _LINUX_SUNRPC_CACHE_H_
15
 
16
#include <linux/slab.h>
17
#include <asm/atomic.h>
18
#include <linux/proc_fs.h>
19
 
20
/*
21
 * Each cache requires:
22
 *  - A 'struct cache_detail' which contains information specific to the cache
23
 *    for common code to use.
24
 *  - An item structure that must contain a "struct cache_head"
25
 *  - A lookup function defined using DefineCacheLookup
26
 *  - A 'put' function that can release a cache item. It will only
27
 *    be called after cache_put has succeed, so there are guarantee
28
 *    to be no references.
29
 *  - A function to calculate a hash of an item's key.
30
 *
31
 * as well as assorted code fragments (e.g. compare keys) and numbers
32
 * (e.g. hash size, goal_age, etc).
33
 *
34
 * Each cache must be registered so that it can be cleaned regularly.
35
 * When the cache is unregistered, it is flushed completely.
36
 *
37
 * Entries have a ref count and a 'hashed' flag which counts the existance
38
 * in the hash table.
39
 * We only expire entries when refcount is zero.
40
 * Existance in the cache is not measured in refcount but rather in
41
 * CACHE_HASHED flag.
42
 */
43
 
44
/* Every cache item has a common header that is used
45
 * for expiring and refreshing entries.
46
 *
47
 */
48
struct cache_head {
49
        struct cache_head * next;
50
        time_t          expiry_time;    /* After time time, don't use the data */
51
        time_t          last_refresh;   /* If CACHE_PENDING, this is when upcall
52
                                         * was sent, else this is when update was received
53
                                         */
54
        atomic_t        refcnt;
55
        unsigned long   flags;
56
};
57
#define CACHE_VALID     0       /* Entry contains valid data */
58
#define CACHE_NEGATIVE  1       /* Negative entry - there is no match for the key */
59
#define CACHE_PENDING   2       /* An upcall has been sent but no reply received yet*/
60
#define CACHE_HASHED    3       /* Entry is in a hash table */
61
 
62
#define CACHE_NEW_EXPIRY 120    /* keep new things pending confirmation for 120 seconds */
63
 
64
struct cache_detail {
65
        int                     hash_size;
66
        struct cache_head **    hash_table;
67
        rwlock_t                hash_lock;
68
 
69
        atomic_t                inuse; /* active user-space update or lookup */
70
 
71
        char                    *name;
72
        void                    (*cache_put)(struct cache_head *,
73
                                             struct cache_detail*);
74
 
75
        void                    (*cache_request)(struct cache_detail *cd,
76
                                                 struct cache_head *h,
77
                                                 char **bpp, int *blen);
78
        int                     (*cache_parse)(struct cache_detail *,
79
                                               char *buf, int len);
80
 
81
        int                     (*cache_show)(struct seq_file *m,
82
                                              struct cache_detail *cd,
83
                                              struct cache_head *h);
84
 
85
        /* fields below this comment are for internal use
86
         * and should not be touched by cache owners
87
         */
88
        time_t                  flush_time;             /* flush all cache items with last_refresh
89
                                                         * earlier than this */
90
        struct list_head        others;
91
        time_t                  nextcheck;
92
        int                     entries;
93
 
94
        /* fields for communication over channel */
95
        struct list_head        queue;
96
        struct proc_dir_entry   *proc_ent;
97
        atomic_t                readers;                /* how many time is /chennel open */
98
        time_t                  last_close;             /* it no readers, when did last close */
99
};
100
 
101
 
102
/* this must be embedded in any request structure that
103
 * identifies an object that will want a callback on
104
 * a cache fill
105
 */
106
struct cache_req {
107
        struct cache_deferred_req *(*defer)(struct cache_req *req);
108
};
109
/* this must be embedded in a deferred_request that is being
110
 * delayed awaiting cache-fill
111
 */
112
struct cache_deferred_req {
113
        struct list_head        hash;   /* on hash chain */
114
        struct list_head        recent; /* on fifo */
115
        struct cache_head       *item;  /* cache item we wait on */
116
        time_t                  recv_time;
117
        void                    *owner; /* we might need to discard all defered requests
118
                                         * owned by someone */
119
        void                    (*revisit)(struct cache_deferred_req *req,
120
                                           int too_many);
121
};
122
 
123
/*
124
 * just like a template in C++, this macro does cache lookup
125
 * for us.
126
 * The function is passed some sort of HANDLE from which a cache_detail
127
 * structure can be determined (via SETUP, DETAIL), a template
128
 * cache entry (type RTN*), and a "set" flag.  Using the HASHFN and the
129
 * TEST, the function will try to find a matching cache entry in the cache.
130
 * If "set" == 0 :
131
 *    If an entry is found, it is returned
132
 *    If no entry is found, a new non-VALID entry is created.
133
 * If "set" == 1 :
134
 *    If no entry is found a new one is inserted with data from "template"
135
 *    If a non-CACHE_VALID entry is found, it is updated from template using UPDATE
136
 *    If a CACHE_VALID entry is found, a new entry is swapped in with data
137
 *       from "template"
138
 * If set == 2, we UPDATE, but don't swap. i.e. update in place
139
 *
140
 * If the passed handle has the CACHE_NEGATIVE flag set, then UPDATE is not
141
 * run but insteead CACHE_NEGATIVE is set in any new item.
142
 
143
 *  In any case, the new entry is returned with a reference count.
144
 *
145
 *    
146
 * RTN is a struct type for a cache entry
147
 * MEMBER is the member of the cache which is cache_head, which must be first
148
 * FNAME is the name for the function  
149
 * ARGS are arguments to function and must contain RTN *item, int set.  May
150
 *   also contain something to be usedby SETUP or DETAIL to find cache_detail.
151
 * SETUP  locates the cache detail and makes it available as...
152
 * DETAIL identifies the cache detail, possibly set up by SETUP
153
 * HASHFN returns a hash value of the cache entry "item"
154
 * TEST  tests if "tmp" matches "item"
155
 * INIT copies key information from "item" to "new"
156
 * UPDATE copies content information from "item" to "tmp"
157
 * INPLACE is true if updates can happen inplace rather than allocating a new structure
158
 */
159
#define DefineCacheLookup(RTN,MEMBER,FNAME,ARGS,SETUP,DETAIL,HASHFN,TEST,INIT,UPDATE,INPLACE)   \
160
RTN *FNAME ARGS                                                                         \
161
{                                                                                       \
162
        RTN *tmp, *new=NULL;                                                            \
163
        struct cache_head **hp, **head;                                                 \
164
        SETUP;                                                                          \
165
 retry:                                                                                 \
166
        head = &(DETAIL)->hash_table[HASHFN];                                           \
167
        if (set||new) write_lock(&(DETAIL)->hash_lock);                                 \
168
        else read_lock(&(DETAIL)->hash_lock);                                           \
169
        for(hp=head; *hp != NULL; hp = &tmp->MEMBER.next) {                             \
170
                tmp = container_of(*hp, RTN, MEMBER);                                   \
171
                if (TEST) { /* found a match */                                         \
172
                                                                                        \
173
                        if (set && !INPLACE && test_bit(CACHE_VALID, &tmp->MEMBER.flags) && !new) \
174
                                break;                                                  \
175
                                                                                        \
176
                        cache_get(&tmp->MEMBER);                                        \
177
                        if (set) {                                                      \
178
                                if (!INPLACE && test_bit(CACHE_VALID, &tmp->MEMBER.flags))\
179
                                { /* need to swap in new */                             \
180
                                        RTN *t2;                                        \
181
                                                                                        \
182
                                        new->MEMBER.next = tmp->MEMBER.next;            \
183
                                        *hp = &new->MEMBER;                             \
184
                                        tmp->MEMBER.next = NULL;                        \
185
                                        set_bit(CACHE_HASHED, &new->MEMBER.flags);      \
186
                                        clear_bit(CACHE_HASHED, &tmp->MEMBER.flags);    \
187
                                        t2 = tmp; tmp = new; new = t2;                  \
188
                                }                                                       \
189
                                if (test_bit(CACHE_NEGATIVE,  &item->MEMBER.flags))     \
190
                                         set_bit(CACHE_NEGATIVE, &tmp->MEMBER.flags);   \
191
                                else {UPDATE;}                                          \
192
                        }                                                               \
193
                        if (set||new) write_unlock(&(DETAIL)->hash_lock);               \
194
                        else read_unlock(&(DETAIL)->hash_lock);                         \
195
                        if (set)                                                        \
196
                                cache_fresh(DETAIL, &tmp->MEMBER, item->MEMBER.expiry_time); \
197
                        if (set && !INPLACE && new) cache_fresh(DETAIL, &new->MEMBER, 0);       \
198
                        if (new) (DETAIL)->cache_put(&new->MEMBER, DETAIL);             \
199
                        return tmp;                                                     \
200
                }                                                                       \
201
        }                                                                               \
202
        /* Didn't find anything */                                                      \
203
        if (new) {                                                                      \
204
                new->MEMBER.next = *head;                                               \
205
                *head = &new->MEMBER;                                                   \
206
                (DETAIL)->entries ++;                                                   \
207
                set_bit(CACHE_HASHED, &new->MEMBER.flags);                              \
208
                if (set) {                                                              \
209
                        tmp = new;                                                      \
210
                        if (test_bit(CACHE_NEGATIVE, &item->MEMBER.flags))              \
211
                                set_bit(CACHE_NEGATIVE, &tmp->MEMBER.flags);            \
212
                        else {UPDATE;}                                                  \
213
                }                                                                       \
214
        }                                                                               \
215
        if (set||new) write_unlock(&(DETAIL)->hash_lock);                               \
216
        else read_unlock(&(DETAIL)->hash_lock);                                         \
217
        if (new && set)                                                                 \
218
                cache_fresh(DETAIL, &new->MEMBER, item->MEMBER.expiry_time);            \
219
        if (new)                                                                        \
220
                return new;                                                             \
221
        new = kmalloc(sizeof(*new), GFP_KERNEL);                                        \
222
        if (new) {                                                                      \
223
                cache_init(&new->MEMBER);                                               \
224
                cache_get(&new->MEMBER);                                                \
225
                INIT;                                                                   \
226
                tmp = new;                                                              \
227
                goto retry;                                                             \
228
        }                                                                               \
229
        return NULL;                                                                    \
230
}
231
 
232
#define DefineSimpleCacheLookup(STRUCT,INPLACE) \
233
        DefineCacheLookup(struct STRUCT, h, STRUCT##_lookup, (struct STRUCT *item, int set), /*no setup */,     \
234
                          & STRUCT##_cache, STRUCT##_hash(item), STRUCT##_match(item, tmp),\
235
                          STRUCT##_init(new, item), STRUCT##_update(tmp, item),INPLACE)
236
 
237
#define cache_for_each(pos, detail, index, member)                                              \
238
        for (({read_lock(&(detail)->hash_lock); index = (detail)->hash_size;}) ;                \
239
             ({if (index==0)read_unlock(&(detail)->hash_lock); index--;});                      \
240
                )                                                                               \
241
                for (pos = container_of((detail)->hash_table[index], typeof(*pos), member);     \
242
                     &pos->member;                                                              \
243
                     pos = container_of(pos->member.next, typeof(*pos), member))
244
 
245
 
246
 
247
extern void cache_defer_req(struct cache_req *req, struct cache_head *item);
248
extern void cache_revisit_request(struct cache_head *item);
249
extern void cache_clean_deferred(void *owner);
250
 
251
static inline struct cache_head  *cache_get(struct cache_head *h)
252
{
253
        atomic_inc(&h->refcnt);
254
        return h;
255
}
256
 
257
 
258
static inline int cache_put(struct cache_head *h, struct cache_detail *cd)
259
{
260
        atomic_dec(&h->refcnt);
261
        if (!atomic_read(&h->refcnt) &&
262
            h->expiry_time < cd->nextcheck)
263
                cd->nextcheck = h->expiry_time;
264
        if (!test_bit(CACHE_HASHED, &h->flags) &&
265
            !atomic_read(&h->refcnt))
266
                return 1;
267
 
268
        return 0;
269
}
270
 
271
extern void cache_init(struct cache_head *h);
272
extern void cache_fresh(struct cache_detail *detail,
273
                        struct cache_head *head, time_t expiry);
274
extern int cache_check(struct cache_detail *detail,
275
                       struct cache_head *h, struct cache_req *rqstp);
276
extern int cache_clean(void);
277
extern void cache_flush(void);
278
extern void cache_purge(struct cache_detail *detail);
279
#define NEVER (0x7FFFFFFF)
280
extern void cache_register(struct cache_detail *cd);
281
extern int cache_unregister(struct cache_detail *cd);
282
extern struct cache_detail *cache_find(char *name);
283
extern void cache_drop(struct cache_detail *detail);
284
 
285
extern void qword_add(char **bpp, int *lp, char *str);
286
extern void qword_addhex(char **bpp, int *lp, char *buf, int blen);
287
extern int qword_get(char **bpp, char *dest, int bufsize);
288
 
289
static inline int get_int(char **bpp, int *anint)
290
{
291
        char buf[50];
292
        char *ep;
293
        int rv;
294
        int len = qword_get(bpp, buf, 50);
295
        if (len < 0) return -EINVAL;
296
        if (len ==0) return -ENOENT;
297
        rv = simple_strtol(buf, &ep, 0);
298
        if (*ep) return -EINVAL;
299
        *anint = rv;
300
        return 0;
301
}
302
 
303
static inline time_t get_expiry(char **bpp)
304
{
305
        int rv;
306
        if (get_int(bpp, &rv))
307
                return 0;
308
        if (rv < 0)
309
                return 0;
310
        return rv;
311
}
312
 
313
#endif /*  _LINUX_SUNRPC_CACHE_H_ */