diff options
-rw-r--r-- | include/linux/sunrpc/cache.h | 12 | ||||
-rw-r--r-- | net/sunrpc/cache.c | 98 |
2 files changed, 110 insertions, 0 deletions
diff --git a/include/linux/sunrpc/cache.h b/include/linux/sunrpc/cache.h index 405ac14e509a..3e17a5ff1dea 100644 --- a/include/linux/sunrpc/cache.h +++ b/include/linux/sunrpc/cache.h | |||
@@ -81,6 +81,11 @@ struct cache_detail { | |||
81 | struct cache_detail *cd, | 81 | struct cache_detail *cd, |
82 | struct cache_head *h); | 82 | struct cache_head *h); |
83 | 83 | ||
84 | struct cache_head * (*alloc)(void); | ||
85 | int (*match)(struct cache_head *orig, struct cache_head *new); | ||
86 | void (*init)(struct cache_head *orig, struct cache_head *new); | ||
87 | void (*update)(struct cache_head *orig, struct cache_head *new); | ||
88 | |||
84 | /* fields below this comment are for internal use | 89 | /* fields below this comment are for internal use |
85 | * and should not be touched by cache owners | 90 | * and should not be touched by cache owners |
86 | */ | 91 | */ |
@@ -237,6 +242,13 @@ RTN *FNAME ARGS \ | |||
237 | & FUNC##_cache, FUNC##_hash(item), FUNC##_match(item, tmp), \ | 242 | & FUNC##_cache, FUNC##_hash(item), FUNC##_match(item, tmp), \ |
238 | STRUCT##_init(new, item), STRUCT##_update(tmp, item)) | 243 | STRUCT##_init(new, item), STRUCT##_update(tmp, item)) |
239 | 244 | ||
245 | extern struct cache_head * | ||
246 | sunrpc_cache_lookup(struct cache_detail *detail, | ||
247 | struct cache_head *key, int hash); | ||
248 | extern struct cache_head * | ||
249 | sunrpc_cache_update(struct cache_detail *detail, | ||
250 | struct cache_head *new, struct cache_head *old, int hash); | ||
251 | |||
240 | 252 | ||
241 | #define cache_for_each(pos, detail, index, member) \ | 253 | #define cache_for_each(pos, detail, index, member) \ |
242 | for (({read_lock(&(detail)->hash_lock); index = (detail)->hash_size;}) ; \ | 254 | for (({read_lock(&(detail)->hash_lock); index = (detail)->hash_size;}) ; \ |
diff --git a/net/sunrpc/cache.c b/net/sunrpc/cache.c index 0acccfeeb284..4449dc52edf5 100644 --- a/net/sunrpc/cache.c +++ b/net/sunrpc/cache.c | |||
@@ -47,6 +47,104 @@ void cache_init(struct cache_head *h) | |||
47 | h->last_refresh = now; | 47 | h->last_refresh = now; |
48 | } | 48 | } |
49 | 49 | ||
50 | struct cache_head *sunrpc_cache_lookup(struct cache_detail *detail, | ||
51 | struct cache_head *key, int hash) | ||
52 | { | ||
53 | struct cache_head **head, **hp; | ||
54 | struct cache_head *new = NULL; | ||
55 | |||
56 | head = &detail->hash_table[hash]; | ||
57 | |||
58 | read_lock(&detail->hash_lock); | ||
59 | |||
60 | for (hp=head; *hp != NULL ; hp = &(*hp)->next) { | ||
61 | struct cache_head *tmp = *hp; | ||
62 | if (detail->match(tmp, key)) { | ||
63 | cache_get(tmp); | ||
64 | read_unlock(&detail->hash_lock); | ||
65 | return tmp; | ||
66 | } | ||
67 | } | ||
68 | read_unlock(&detail->hash_lock); | ||
69 | /* Didn't find anything, insert an empty entry */ | ||
70 | |||
71 | new = detail->alloc(); | ||
72 | if (!new) | ||
73 | return NULL; | ||
74 | cache_init(new); | ||
75 | |||
76 | write_lock(&detail->hash_lock); | ||
77 | |||
78 | /* check if entry appeared while we slept */ | ||
79 | for (hp=head; *hp != NULL ; hp = &(*hp)->next) { | ||
80 | struct cache_head *tmp = *hp; | ||
81 | if (detail->match(tmp, key)) { | ||
82 | cache_get(tmp); | ||
83 | write_unlock(&detail->hash_lock); | ||
84 | detail->cache_put(new, detail); | ||
85 | return tmp; | ||
86 | } | ||
87 | } | ||
88 | detail->init(new, key); | ||
89 | new->next = *head; | ||
90 | *head = new; | ||
91 | detail->entries++; | ||
92 | cache_get(new); | ||
93 | write_unlock(&detail->hash_lock); | ||
94 | |||
95 | return new; | ||
96 | } | ||
97 | EXPORT_SYMBOL(sunrpc_cache_lookup); | ||
98 | |||
99 | struct cache_head *sunrpc_cache_update(struct cache_detail *detail, | ||
100 | struct cache_head *new, struct cache_head *old, int hash) | ||
101 | { | ||
102 | /* The 'old' entry is to be replaced by 'new'. | ||
103 | * If 'old' is not VALID, we update it directly, | ||
104 | * otherwise we need to replace it | ||
105 | */ | ||
106 | struct cache_head **head; | ||
107 | struct cache_head *tmp; | ||
108 | |||
109 | if (!test_bit(CACHE_VALID, &old->flags)) { | ||
110 | write_lock(&detail->hash_lock); | ||
111 | if (!test_bit(CACHE_VALID, &old->flags)) { | ||
112 | if (test_bit(CACHE_NEGATIVE, &new->flags)) | ||
113 | set_bit(CACHE_NEGATIVE, &old->flags); | ||
114 | else | ||
115 | detail->update(old, new); | ||
116 | /* FIXME cache_fresh should come first */ | ||
117 | write_unlock(&detail->hash_lock); | ||
118 | cache_fresh(detail, old, new->expiry_time); | ||
119 | return old; | ||
120 | } | ||
121 | write_unlock(&detail->hash_lock); | ||
122 | } | ||
123 | /* We need to insert a new entry */ | ||
124 | tmp = detail->alloc(); | ||
125 | if (!tmp) { | ||
126 | detail->cache_put(old, detail); | ||
127 | return NULL; | ||
128 | } | ||
129 | cache_init(tmp); | ||
130 | detail->init(tmp, old); | ||
131 | head = &detail->hash_table[hash]; | ||
132 | |||
133 | write_lock(&detail->hash_lock); | ||
134 | if (test_bit(CACHE_NEGATIVE, &new->flags)) | ||
135 | set_bit(CACHE_NEGATIVE, &tmp->flags); | ||
136 | else | ||
137 | detail->update(tmp, new); | ||
138 | tmp->next = *head; | ||
139 | *head = tmp; | ||
140 | cache_get(tmp); | ||
141 | write_unlock(&detail->hash_lock); | ||
142 | cache_fresh(detail, tmp, new->expiry_time); | ||
143 | cache_fresh(detail, old, 0); | ||
144 | detail->cache_put(old, detail); | ||
145 | return tmp; | ||
146 | } | ||
147 | EXPORT_SYMBOL(sunrpc_cache_update); | ||
50 | 148 | ||
51 | static int cache_make_upcall(struct cache_detail *detail, struct cache_head *h); | 149 | static int cache_make_upcall(struct cache_detail *detail, struct cache_head *h); |
52 | /* | 150 | /* |