|
@@ -54,28 +54,33 @@ static void cache_init(struct cache_head *h, struct cache_detail *detail)
|
|
|
h->last_refresh = now;
|
|
|
}
|
|
|
|
|
|
-struct cache_head *sunrpc_cache_lookup(struct cache_detail *detail,
|
|
|
- struct cache_head *key, int hash)
|
|
|
+static struct cache_head *sunrpc_cache_find_rcu(struct cache_detail *detail,
|
|
|
+ struct cache_head *key,
|
|
|
+ int hash)
|
|
|
{
|
|
|
- struct cache_head *new = NULL, *freeme = NULL, *tmp = NULL;
|
|
|
- struct hlist_head *head;
|
|
|
-
|
|
|
- head = &detail->hash_table[hash];
|
|
|
-
|
|
|
- read_lock(&detail->hash_lock);
|
|
|
+ struct hlist_head *head = &detail->hash_table[hash];
|
|
|
+ struct cache_head *tmp;
|
|
|
|
|
|
- hlist_for_each_entry(tmp, head, cache_list) {
|
|
|
+ rcu_read_lock();
|
|
|
+ hlist_for_each_entry_rcu(tmp, head, cache_list) {
|
|
|
if (detail->match(tmp, key)) {
|
|
|
if (cache_is_expired(detail, tmp))
|
|
|
- /* This entry is expired, we will discard it. */
|
|
|
- break;
|
|
|
- cache_get(tmp);
|
|
|
- read_unlock(&detail->hash_lock);
|
|
|
+ continue;
|
|
|
+ tmp = cache_get_rcu(tmp);
|
|
|
+ rcu_read_unlock();
|
|
|
return tmp;
|
|
|
}
|
|
|
}
|
|
|
- read_unlock(&detail->hash_lock);
|
|
|
- /* Didn't find anything, insert an empty entry */
|
|
|
+ rcu_read_unlock();
|
|
|
+ return NULL;
|
|
|
+}
|
|
|
+
|
|
|
+static struct cache_head *sunrpc_cache_add_entry(struct cache_detail *detail,
|
|
|
+ struct cache_head *key,
|
|
|
+ int hash)
|
|
|
+{
|
|
|
+ struct cache_head *new, *tmp, *freeme = NULL;
|
|
|
+ struct hlist_head *head = &detail->hash_table[hash];
|
|
|
|
|
|
new = detail->alloc();
|
|
|
if (!new)
|
|
@@ -87,35 +92,46 @@ struct cache_head *sunrpc_cache_lookup(struct cache_detail *detail,
|
|
|
cache_init(new, detail);
|
|
|
detail->init(new, key);
|
|
|
|
|
|
- write_lock(&detail->hash_lock);
|
|
|
+ spin_lock(&detail->hash_lock);
|
|
|
|
|
|
/* check if entry appeared while we slept */
|
|
|
- hlist_for_each_entry(tmp, head, cache_list) {
|
|
|
+ hlist_for_each_entry_rcu(tmp, head, cache_list) {
|
|
|
if (detail->match(tmp, key)) {
|
|
|
if (cache_is_expired(detail, tmp)) {
|
|
|
- hlist_del_init(&tmp->cache_list);
|
|
|
+ hlist_del_init_rcu(&tmp->cache_list);
|
|
|
detail->entries --;
|
|
|
freeme = tmp;
|
|
|
break;
|
|
|
}
|
|
|
cache_get(tmp);
|
|
|
- write_unlock(&detail->hash_lock);
|
|
|
+ spin_unlock(&detail->hash_lock);
|
|
|
cache_put(new, detail);
|
|
|
return tmp;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- hlist_add_head(&new->cache_list, head);
|
|
|
+ hlist_add_head_rcu(&new->cache_list, head);
|
|
|
detail->entries++;
|
|
|
cache_get(new);
|
|
|
- write_unlock(&detail->hash_lock);
|
|
|
+ spin_unlock(&detail->hash_lock);
|
|
|
|
|
|
if (freeme)
|
|
|
cache_put(freeme, detail);
|
|
|
return new;
|
|
|
}
|
|
|
-EXPORT_SYMBOL_GPL(sunrpc_cache_lookup);
|
|
|
|
|
|
+struct cache_head *sunrpc_cache_lookup_rcu(struct cache_detail *detail,
|
|
|
+ struct cache_head *key, int hash)
|
|
|
+{
|
|
|
+ struct cache_head *ret;
|
|
|
+
|
|
|
+ ret = sunrpc_cache_find_rcu(detail, key, hash);
|
|
|
+ if (ret)
|
|
|
+ return ret;
|
|
|
+ /* Didn't find anything, insert an empty entry */
|
|
|
+ return sunrpc_cache_add_entry(detail, key, hash);
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(sunrpc_cache_lookup_rcu);
|
|
|
|
|
|
static void cache_dequeue(struct cache_detail *detail, struct cache_head *ch);
|
|
|
|
|
@@ -151,18 +167,18 @@ struct cache_head *sunrpc_cache_update(struct cache_detail *detail,
|
|
|
struct cache_head *tmp;
|
|
|
|
|
|
if (!test_bit(CACHE_VALID, &old->flags)) {
|
|
|
- write_lock(&detail->hash_lock);
|
|
|
+ spin_lock(&detail->hash_lock);
|
|
|
if (!test_bit(CACHE_VALID, &old->flags)) {
|
|
|
if (test_bit(CACHE_NEGATIVE, &new->flags))
|
|
|
set_bit(CACHE_NEGATIVE, &old->flags);
|
|
|
else
|
|
|
detail->update(old, new);
|
|
|
cache_fresh_locked(old, new->expiry_time, detail);
|
|
|
- write_unlock(&detail->hash_lock);
|
|
|
+ spin_unlock(&detail->hash_lock);
|
|
|
cache_fresh_unlocked(old, detail);
|
|
|
return old;
|
|
|
}
|
|
|
- write_unlock(&detail->hash_lock);
|
|
|
+ spin_unlock(&detail->hash_lock);
|
|
|
}
|
|
|
/* We need to insert a new entry */
|
|
|
tmp = detail->alloc();
|
|
@@ -173,7 +189,7 @@ struct cache_head *sunrpc_cache_update(struct cache_detail *detail,
|
|
|
cache_init(tmp, detail);
|
|
|
detail->init(tmp, old);
|
|
|
|
|
|
- write_lock(&detail->hash_lock);
|
|
|
+ spin_lock(&detail->hash_lock);
|
|
|
if (test_bit(CACHE_NEGATIVE, &new->flags))
|
|
|
set_bit(CACHE_NEGATIVE, &tmp->flags);
|
|
|
else
|
|
@@ -183,7 +199,7 @@ struct cache_head *sunrpc_cache_update(struct cache_detail *detail,
|
|
|
cache_get(tmp);
|
|
|
cache_fresh_locked(tmp, new->expiry_time, detail);
|
|
|
cache_fresh_locked(old, 0, detail);
|
|
|
- write_unlock(&detail->hash_lock);
|
|
|
+ spin_unlock(&detail->hash_lock);
|
|
|
cache_fresh_unlocked(tmp, detail);
|
|
|
cache_fresh_unlocked(old, detail);
|
|
|
cache_put(old, detail);
|
|
@@ -223,7 +239,7 @@ static int try_to_negate_entry(struct cache_detail *detail, struct cache_head *h
|
|
|
{
|
|
|
int rv;
|
|
|
|
|
|
- write_lock(&detail->hash_lock);
|
|
|
+ spin_lock(&detail->hash_lock);
|
|
|
rv = cache_is_valid(h);
|
|
|
if (rv == -EAGAIN) {
|
|
|
set_bit(CACHE_NEGATIVE, &h->flags);
|
|
@@ -231,7 +247,7 @@ static int try_to_negate_entry(struct cache_detail *detail, struct cache_head *h
|
|
|
detail);
|
|
|
rv = -ENOENT;
|
|
|
}
|
|
|
- write_unlock(&detail->hash_lock);
|
|
|
+ spin_unlock(&detail->hash_lock);
|
|
|
cache_fresh_unlocked(h, detail);
|
|
|
return rv;
|
|
|
}
|
|
@@ -341,7 +357,7 @@ static struct delayed_work cache_cleaner;
|
|
|
|
|
|
void sunrpc_init_cache_detail(struct cache_detail *cd)
|
|
|
{
|
|
|
- rwlock_init(&cd->hash_lock);
|
|
|
+ spin_lock_init(&cd->hash_lock);
|
|
|
INIT_LIST_HEAD(&cd->queue);
|
|
|
spin_lock(&cache_list_lock);
|
|
|
cd->nextcheck = 0;
|
|
@@ -361,11 +377,11 @@ void sunrpc_destroy_cache_detail(struct cache_detail *cd)
|
|
|
{
|
|
|
cache_purge(cd);
|
|
|
spin_lock(&cache_list_lock);
|
|
|
- write_lock(&cd->hash_lock);
|
|
|
+ spin_lock(&cd->hash_lock);
|
|
|
if (current_detail == cd)
|
|
|
current_detail = NULL;
|
|
|
list_del_init(&cd->others);
|
|
|
- write_unlock(&cd->hash_lock);
|
|
|
+ spin_unlock(&cd->hash_lock);
|
|
|
spin_unlock(&cache_list_lock);
|
|
|
if (list_empty(&cache_list)) {
|
|
|
/* module must be being unloaded so its safe to kill the worker */
|
|
@@ -422,7 +438,7 @@ static int cache_clean(void)
|
|
|
struct hlist_head *head;
|
|
|
struct hlist_node *tmp;
|
|
|
|
|
|
- write_lock(¤t_detail->hash_lock);
|
|
|
+ spin_lock(¤t_detail->hash_lock);
|
|
|
|
|
|
/* Ok, now to clean this strand */
|
|
|
|
|
@@ -433,13 +449,13 @@ static int cache_clean(void)
|
|
|
if (!cache_is_expired(current_detail, ch))
|
|
|
continue;
|
|
|
|
|
|
- hlist_del_init(&ch->cache_list);
|
|
|
+ hlist_del_init_rcu(&ch->cache_list);
|
|
|
current_detail->entries--;
|
|
|
rv = 1;
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
- write_unlock(¤t_detail->hash_lock);
|
|
|
+ spin_unlock(¤t_detail->hash_lock);
|
|
|
d = current_detail;
|
|
|
if (!ch)
|
|
|
current_index ++;
|
|
@@ -494,9 +510,9 @@ void cache_purge(struct cache_detail *detail)
|
|
|
struct hlist_node *tmp = NULL;
|
|
|
int i = 0;
|
|
|
|
|
|
- write_lock(&detail->hash_lock);
|
|
|
+ spin_lock(&detail->hash_lock);
|
|
|
if (!detail->entries) {
|
|
|
- write_unlock(&detail->hash_lock);
|
|
|
+ spin_unlock(&detail->hash_lock);
|
|
|
return;
|
|
|
}
|
|
|
|
|
@@ -504,17 +520,17 @@ void cache_purge(struct cache_detail *detail)
|
|
|
for (i = 0; i < detail->hash_size; i++) {
|
|
|
head = &detail->hash_table[i];
|
|
|
hlist_for_each_entry_safe(ch, tmp, head, cache_list) {
|
|
|
- hlist_del_init(&ch->cache_list);
|
|
|
+ hlist_del_init_rcu(&ch->cache_list);
|
|
|
detail->entries--;
|
|
|
|
|
|
set_bit(CACHE_CLEANED, &ch->flags);
|
|
|
- write_unlock(&detail->hash_lock);
|
|
|
+ spin_unlock(&detail->hash_lock);
|
|
|
cache_fresh_unlocked(ch, detail);
|
|
|
cache_put(ch, detail);
|
|
|
- write_lock(&detail->hash_lock);
|
|
|
+ spin_lock(&detail->hash_lock);
|
|
|
}
|
|
|
}
|
|
|
- write_unlock(&detail->hash_lock);
|
|
|
+ spin_unlock(&detail->hash_lock);
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(cache_purge);
|
|
|
|
|
@@ -1289,21 +1305,19 @@ EXPORT_SYMBOL_GPL(qword_get);
|
|
|
* get a header, then pass each real item in the cache
|
|
|
*/
|
|
|
|
|
|
-void *cache_seq_start(struct seq_file *m, loff_t *pos)
|
|
|
- __acquires(cd->hash_lock)
|
|
|
+static void *__cache_seq_start(struct seq_file *m, loff_t *pos)
|
|
|
{
|
|
|
loff_t n = *pos;
|
|
|
unsigned int hash, entry;
|
|
|
struct cache_head *ch;
|
|
|
struct cache_detail *cd = m->private;
|
|
|
|
|
|
- read_lock(&cd->hash_lock);
|
|
|
if (!n--)
|
|
|
return SEQ_START_TOKEN;
|
|
|
hash = n >> 32;
|
|
|
entry = n & ((1LL<<32) - 1);
|
|
|
|
|
|
- hlist_for_each_entry(ch, &cd->hash_table[hash], cache_list)
|
|
|
+ hlist_for_each_entry_rcu(ch, &cd->hash_table[hash], cache_list)
|
|
|
if (!entry--)
|
|
|
return ch;
|
|
|
n &= ~((1LL<<32) - 1);
|
|
@@ -1315,12 +1329,12 @@ void *cache_seq_start(struct seq_file *m, loff_t *pos)
|
|
|
if (hash >= cd->hash_size)
|
|
|
return NULL;
|
|
|
*pos = n+1;
|
|
|
- return hlist_entry_safe(cd->hash_table[hash].first,
|
|
|
+ return hlist_entry_safe(rcu_dereference_raw(
|
|
|
+ hlist_first_rcu(&cd->hash_table[hash])),
|
|
|
struct cache_head, cache_list);
|
|
|
}
|
|
|
-EXPORT_SYMBOL_GPL(cache_seq_start);
|
|
|
|
|
|
-void *cache_seq_next(struct seq_file *m, void *p, loff_t *pos)
|
|
|
+static void *cache_seq_next(struct seq_file *m, void *p, loff_t *pos)
|
|
|
{
|
|
|
struct cache_head *ch = p;
|
|
|
int hash = (*pos >> 32);
|
|
@@ -1333,7 +1347,8 @@ void *cache_seq_next(struct seq_file *m, void *p, loff_t *pos)
|
|
|
*pos += 1LL<<32;
|
|
|
} else {
|
|
|
++*pos;
|
|
|
- return hlist_entry_safe(ch->cache_list.next,
|
|
|
+ return hlist_entry_safe(rcu_dereference_raw(
|
|
|
+ hlist_next_rcu(&ch->cache_list)),
|
|
|
struct cache_head, cache_list);
|
|
|
}
|
|
|
*pos &= ~((1LL<<32) - 1);
|
|
@@ -1345,18 +1360,32 @@ void *cache_seq_next(struct seq_file *m, void *p, loff_t *pos)
|
|
|
if (hash >= cd->hash_size)
|
|
|
return NULL;
|
|
|
++*pos;
|
|
|
- return hlist_entry_safe(cd->hash_table[hash].first,
|
|
|
+ return hlist_entry_safe(rcu_dereference_raw(
|
|
|
+ hlist_first_rcu(&cd->hash_table[hash])),
|
|
|
struct cache_head, cache_list);
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(cache_seq_next);
|
|
|
|
|
|
-void cache_seq_stop(struct seq_file *m, void *p)
|
|
|
- __releases(cd->hash_lock)
|
|
|
+void *cache_seq_start_rcu(struct seq_file *m, loff_t *pos)
|
|
|
+ __acquires(RCU)
|
|
|
{
|
|
|
- struct cache_detail *cd = m->private;
|
|
|
- read_unlock(&cd->hash_lock);
|
|
|
+ rcu_read_lock();
|
|
|
+ return __cache_seq_start(m, pos);
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(cache_seq_start_rcu);
|
|
|
+
|
|
|
+void *cache_seq_next_rcu(struct seq_file *file, void *p, loff_t *pos)
|
|
|
+{
|
|
|
+ return cache_seq_next(file, p, pos);
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(cache_seq_next_rcu);
|
|
|
+
|
|
|
+void cache_seq_stop_rcu(struct seq_file *m, void *p)
|
|
|
+ __releases(RCU)
|
|
|
+{
|
|
|
+ rcu_read_unlock();
|
|
|
}
|
|
|
-EXPORT_SYMBOL_GPL(cache_seq_stop);
|
|
|
+EXPORT_SYMBOL_GPL(cache_seq_stop_rcu);
|
|
|
|
|
|
static int c_show(struct seq_file *m, void *p)
|
|
|
{
|
|
@@ -1384,9 +1413,9 @@ static int c_show(struct seq_file *m, void *p)
|
|
|
}
|
|
|
|
|
|
static const struct seq_operations cache_content_op = {
|
|
|
- .start = cache_seq_start,
|
|
|
- .next = cache_seq_next,
|
|
|
- .stop = cache_seq_stop,
|
|
|
+ .start = cache_seq_start_rcu,
|
|
|
+ .next = cache_seq_next_rcu,
|
|
|
+ .stop = cache_seq_stop_rcu,
|
|
|
.show = c_show,
|
|
|
};
|
|
|
|
|
@@ -1844,13 +1873,13 @@ EXPORT_SYMBOL_GPL(sunrpc_cache_unregister_pipefs);
|
|
|
|
|
|
void sunrpc_cache_unhash(struct cache_detail *cd, struct cache_head *h)
|
|
|
{
|
|
|
- write_lock(&cd->hash_lock);
|
|
|
+ spin_lock(&cd->hash_lock);
|
|
|
if (!hlist_unhashed(&h->cache_list)){
|
|
|
- hlist_del_init(&h->cache_list);
|
|
|
+ hlist_del_init_rcu(&h->cache_list);
|
|
|
cd->entries--;
|
|
|
- write_unlock(&cd->hash_lock);
|
|
|
+ spin_unlock(&cd->hash_lock);
|
|
|
cache_put(h, cd);
|
|
|
} else
|
|
|
- write_unlock(&cd->hash_lock);
|
|
|
+ spin_unlock(&cd->hash_lock);
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(sunrpc_cache_unhash);
|