aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTrond Myklebust <trondmy@gmail.com>2018-10-03 12:01:22 -0400
committerJ. Bruce Fields <bfields@redhat.com>2018-10-29 16:57:59 -0400
commitae74136b4bb64440a55117e12065b8c282ab6c1a (patch)
tree1f1c8cd8581aac2bcc3dc037aaed5387c85966dc
parentb92a8fababa9d18910a54b957be55fef7f603531 (diff)
SUNRPC: Allow cache lookups to use RCU protection rather than the r/w spinlock
Instead of the reader/writer spinlock, allow cache lookups to use RCU for looking up entries. This is more efficient since modifications can occur while other entries are being looked up. Note that for now, we keep the reader/writer spinlock until all users have been converted to use RCU-safe freeing of their cache entries. Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com> Signed-off-by: J. Bruce Fields <bfields@redhat.com>
-rw-r--r--include/linux/sunrpc/cache.h12
-rw-r--r--net/sunrpc/cache.c93
2 files changed, 91 insertions, 14 deletions
diff --git a/include/linux/sunrpc/cache.h b/include/linux/sunrpc/cache.h
index 40d2822f0e2f..cf3e17ee2786 100644
--- a/include/linux/sunrpc/cache.h
+++ b/include/linux/sunrpc/cache.h
@@ -168,6 +168,9 @@ extern const struct file_operations content_file_operations_pipefs;
168extern const struct file_operations cache_flush_operations_pipefs; 168extern const struct file_operations cache_flush_operations_pipefs;
169 169
170extern struct cache_head * 170extern struct cache_head *
171sunrpc_cache_lookup_rcu(struct cache_detail *detail,
172 struct cache_head *key, int hash);
173extern struct cache_head *
171sunrpc_cache_lookup(struct cache_detail *detail, 174sunrpc_cache_lookup(struct cache_detail *detail,
172 struct cache_head *key, int hash); 175 struct cache_head *key, int hash);
173extern struct cache_head * 176extern struct cache_head *
@@ -186,6 +189,12 @@ static inline struct cache_head *cache_get(struct cache_head *h)
186 return h; 189 return h;
187} 190}
188 191
192static inline struct cache_head *cache_get_rcu(struct cache_head *h)
193{
194 if (kref_get_unless_zero(&h->ref))
195 return h;
196 return NULL;
197}
189 198
190static inline void cache_put(struct cache_head *h, struct cache_detail *cd) 199static inline void cache_put(struct cache_head *h, struct cache_detail *cd)
191{ 200{
@@ -227,6 +236,9 @@ extern void sunrpc_cache_unhash(struct cache_detail *, struct cache_head *);
227extern void *cache_seq_start(struct seq_file *file, loff_t *pos); 236extern void *cache_seq_start(struct seq_file *file, loff_t *pos);
228extern void *cache_seq_next(struct seq_file *file, void *p, loff_t *pos); 237extern void *cache_seq_next(struct seq_file *file, void *p, loff_t *pos);
229extern void cache_seq_stop(struct seq_file *file, void *p); 238extern void cache_seq_stop(struct seq_file *file, void *p);
239extern void *cache_seq_start_rcu(struct seq_file *file, loff_t *pos);
240extern void *cache_seq_next_rcu(struct seq_file *file, void *p, loff_t *pos);
241extern void cache_seq_stop_rcu(struct seq_file *file, void *p);
230 242
231extern void qword_add(char **bpp, int *lp, char *str); 243extern void qword_add(char **bpp, int *lp, char *str);
232extern void qword_addhex(char **bpp, int *lp, char *buf, int blen); 244extern void qword_addhex(char **bpp, int *lp, char *buf, int blen);
diff --git a/net/sunrpc/cache.c b/net/sunrpc/cache.c
index aa8e62d61f4d..7593afed9036 100644
--- a/net/sunrpc/cache.c
+++ b/net/sunrpc/cache.c
@@ -54,6 +54,27 @@ static void cache_init(struct cache_head *h, struct cache_detail *detail)
54 h->last_refresh = now; 54 h->last_refresh = now;
55} 55}
56 56
57static struct cache_head *sunrpc_cache_find_rcu(struct cache_detail *detail,
58 struct cache_head *key,
59 int hash)
60{
61 struct hlist_head *head = &detail->hash_table[hash];
62 struct cache_head *tmp;
63
64 rcu_read_lock();
65 hlist_for_each_entry_rcu(tmp, head, cache_list) {
66 if (detail->match(tmp, key)) {
67 if (cache_is_expired(detail, tmp))
68 continue;
69 tmp = cache_get_rcu(tmp);
70 rcu_read_unlock();
71 return tmp;
72 }
73 }
74 rcu_read_unlock();
75 return NULL;
76}
77
57static struct cache_head *sunrpc_cache_find(struct cache_detail *detail, 78static struct cache_head *sunrpc_cache_find(struct cache_detail *detail,
58 struct cache_head *key, int hash) 79 struct cache_head *key, int hash)
59{ 80{
@@ -61,7 +82,6 @@ static struct cache_head *sunrpc_cache_find(struct cache_detail *detail,
61 struct cache_head *tmp; 82 struct cache_head *tmp;
62 83
63 read_lock(&detail->hash_lock); 84 read_lock(&detail->hash_lock);
64
65 hlist_for_each_entry(tmp, head, cache_list) { 85 hlist_for_each_entry(tmp, head, cache_list) {
66 if (detail->match(tmp, key)) { 86 if (detail->match(tmp, key)) {
67 if (cache_is_expired(detail, tmp)) 87 if (cache_is_expired(detail, tmp))
@@ -96,10 +116,10 @@ static struct cache_head *sunrpc_cache_add_entry(struct cache_detail *detail,
96 write_lock(&detail->hash_lock); 116 write_lock(&detail->hash_lock);
97 117
98 /* check if entry appeared while we slept */ 118 /* check if entry appeared while we slept */
99 hlist_for_each_entry(tmp, head, cache_list) { 119 hlist_for_each_entry_rcu(tmp, head, cache_list) {
100 if (detail->match(tmp, key)) { 120 if (detail->match(tmp, key)) {
101 if (cache_is_expired(detail, tmp)) { 121 if (cache_is_expired(detail, tmp)) {
102 hlist_del_init(&tmp->cache_list); 122 hlist_del_init_rcu(&tmp->cache_list);
103 detail->entries --; 123 detail->entries --;
104 freeme = tmp; 124 freeme = tmp;
105 break; 125 break;
@@ -111,7 +131,7 @@ static struct cache_head *sunrpc_cache_add_entry(struct cache_detail *detail,
111 } 131 }
112 } 132 }
113 133
114 hlist_add_head(&new->cache_list, head); 134 hlist_add_head_rcu(&new->cache_list, head);
115 detail->entries++; 135 detail->entries++;
116 cache_get(new); 136 cache_get(new);
117 write_unlock(&detail->hash_lock); 137 write_unlock(&detail->hash_lock);
@@ -121,6 +141,19 @@ static struct cache_head *sunrpc_cache_add_entry(struct cache_detail *detail,
121 return new; 141 return new;
122} 142}
123 143
144struct cache_head *sunrpc_cache_lookup_rcu(struct cache_detail *detail,
145 struct cache_head *key, int hash)
146{
147 struct cache_head *ret;
148
149 ret = sunrpc_cache_find_rcu(detail, key, hash);
150 if (ret)
151 return ret;
152 /* Didn't find anything, insert an empty entry */
153 return sunrpc_cache_add_entry(detail, key, hash);
154}
155EXPORT_SYMBOL_GPL(sunrpc_cache_lookup_rcu);
156
124struct cache_head *sunrpc_cache_lookup(struct cache_detail *detail, 157struct cache_head *sunrpc_cache_lookup(struct cache_detail *detail,
125 struct cache_head *key, int hash) 158 struct cache_head *key, int hash)
126{ 159{
@@ -134,6 +167,7 @@ struct cache_head *sunrpc_cache_lookup(struct cache_detail *detail,
134} 167}
135EXPORT_SYMBOL_GPL(sunrpc_cache_lookup); 168EXPORT_SYMBOL_GPL(sunrpc_cache_lookup);
136 169
170
137static void cache_dequeue(struct cache_detail *detail, struct cache_head *ch); 171static void cache_dequeue(struct cache_detail *detail, struct cache_head *ch);
138 172
139static void cache_fresh_locked(struct cache_head *head, time_t expiry, 173static void cache_fresh_locked(struct cache_head *head, time_t expiry,
@@ -450,7 +484,7 @@ static int cache_clean(void)
450 if (!cache_is_expired(current_detail, ch)) 484 if (!cache_is_expired(current_detail, ch))
451 continue; 485 continue;
452 486
453 hlist_del_init(&ch->cache_list); 487 hlist_del_init_rcu(&ch->cache_list);
454 current_detail->entries--; 488 current_detail->entries--;
455 rv = 1; 489 rv = 1;
456 break; 490 break;
@@ -521,7 +555,7 @@ void cache_purge(struct cache_detail *detail)
521 for (i = 0; i < detail->hash_size; i++) { 555 for (i = 0; i < detail->hash_size; i++) {
522 head = &detail->hash_table[i]; 556 head = &detail->hash_table[i];
523 hlist_for_each_entry_safe(ch, tmp, head, cache_list) { 557 hlist_for_each_entry_safe(ch, tmp, head, cache_list) {
524 hlist_del_init(&ch->cache_list); 558 hlist_del_init_rcu(&ch->cache_list);
525 detail->entries--; 559 detail->entries--;
526 560
527 set_bit(CACHE_CLEANED, &ch->flags); 561 set_bit(CACHE_CLEANED, &ch->flags);
@@ -1306,21 +1340,19 @@ EXPORT_SYMBOL_GPL(qword_get);
1306 * get a header, then pass each real item in the cache 1340 * get a header, then pass each real item in the cache
1307 */ 1341 */
1308 1342
1309void *cache_seq_start(struct seq_file *m, loff_t *pos) 1343static void *__cache_seq_start(struct seq_file *m, loff_t *pos)
1310 __acquires(cd->hash_lock)
1311{ 1344{
1312 loff_t n = *pos; 1345 loff_t n = *pos;
1313 unsigned int hash, entry; 1346 unsigned int hash, entry;
1314 struct cache_head *ch; 1347 struct cache_head *ch;
1315 struct cache_detail *cd = m->private; 1348 struct cache_detail *cd = m->private;
1316 1349
1317 read_lock(&cd->hash_lock);
1318 if (!n--) 1350 if (!n--)
1319 return SEQ_START_TOKEN; 1351 return SEQ_START_TOKEN;
1320 hash = n >> 32; 1352 hash = n >> 32;
1321 entry = n & ((1LL<<32) - 1); 1353 entry = n & ((1LL<<32) - 1);
1322 1354
1323 hlist_for_each_entry(ch, &cd->hash_table[hash], cache_list) 1355 hlist_for_each_entry_rcu(ch, &cd->hash_table[hash], cache_list)
1324 if (!entry--) 1356 if (!entry--)
1325 return ch; 1357 return ch;
1326 n &= ~((1LL<<32) - 1); 1358 n &= ~((1LL<<32) - 1);
@@ -1332,9 +1364,19 @@ void *cache_seq_start(struct seq_file *m, loff_t *pos)
1332 if (hash >= cd->hash_size) 1364 if (hash >= cd->hash_size)
1333 return NULL; 1365 return NULL;
1334 *pos = n+1; 1366 *pos = n+1;
1335 return hlist_entry_safe(cd->hash_table[hash].first, 1367 return hlist_entry_safe(rcu_dereference_raw(
1368 hlist_first_rcu(&cd->hash_table[hash])),
1336 struct cache_head, cache_list); 1369 struct cache_head, cache_list);
1337} 1370}
1371
1372void *cache_seq_start(struct seq_file *m, loff_t *pos)
1373 __acquires(cd->hash_lock)
1374{
1375 struct cache_detail *cd = m->private;
1376
1377 read_lock(&cd->hash_lock);
1378 return __cache_seq_start(m, pos);
1379}
1338EXPORT_SYMBOL_GPL(cache_seq_start); 1380EXPORT_SYMBOL_GPL(cache_seq_start);
1339 1381
1340void *cache_seq_next(struct seq_file *m, void *p, loff_t *pos) 1382void *cache_seq_next(struct seq_file *m, void *p, loff_t *pos)
@@ -1350,7 +1392,8 @@ void *cache_seq_next(struct seq_file *m, void *p, loff_t *pos)
1350 *pos += 1LL<<32; 1392 *pos += 1LL<<32;
1351 } else { 1393 } else {
1352 ++*pos; 1394 ++*pos;
1353 return hlist_entry_safe(ch->cache_list.next, 1395 return hlist_entry_safe(rcu_dereference_raw(
1396 hlist_next_rcu(&ch->cache_list)),
1354 struct cache_head, cache_list); 1397 struct cache_head, cache_list);
1355 } 1398 }
1356 *pos &= ~((1LL<<32) - 1); 1399 *pos &= ~((1LL<<32) - 1);
@@ -1362,7 +1405,8 @@ void *cache_seq_next(struct seq_file *m, void *p, loff_t *pos)
1362 if (hash >= cd->hash_size) 1405 if (hash >= cd->hash_size)
1363 return NULL; 1406 return NULL;
1364 ++*pos; 1407 ++*pos;
1365 return hlist_entry_safe(cd->hash_table[hash].first, 1408 return hlist_entry_safe(rcu_dereference_raw(
1409 hlist_first_rcu(&cd->hash_table[hash])),
1366 struct cache_head, cache_list); 1410 struct cache_head, cache_list);
1367} 1411}
1368EXPORT_SYMBOL_GPL(cache_seq_next); 1412EXPORT_SYMBOL_GPL(cache_seq_next);
@@ -1375,6 +1419,27 @@ void cache_seq_stop(struct seq_file *m, void *p)
1375} 1419}
1376EXPORT_SYMBOL_GPL(cache_seq_stop); 1420EXPORT_SYMBOL_GPL(cache_seq_stop);
1377 1421
1422void *cache_seq_start_rcu(struct seq_file *m, loff_t *pos)
1423 __acquires(RCU)
1424{
1425 rcu_read_lock();
1426 return __cache_seq_start(m, pos);
1427}
1428EXPORT_SYMBOL_GPL(cache_seq_start_rcu);
1429
1430void *cache_seq_next_rcu(struct seq_file *file, void *p, loff_t *pos)
1431{
1432 return cache_seq_next(file, p, pos);
1433}
1434EXPORT_SYMBOL_GPL(cache_seq_next_rcu);
1435
1436void cache_seq_stop_rcu(struct seq_file *m, void *p)
1437 __releases(RCU)
1438{
1439 rcu_read_unlock();
1440}
1441EXPORT_SYMBOL_GPL(cache_seq_stop_rcu);
1442
1378static int c_show(struct seq_file *m, void *p) 1443static int c_show(struct seq_file *m, void *p)
1379{ 1444{
1380 struct cache_head *cp = p; 1445 struct cache_head *cp = p;
@@ -1863,7 +1928,7 @@ void sunrpc_cache_unhash(struct cache_detail *cd, struct cache_head *h)
1863{ 1928{
1864 write_lock(&cd->hash_lock); 1929 write_lock(&cd->hash_lock);
1865 if (!hlist_unhashed(&h->cache_list)){ 1930 if (!hlist_unhashed(&h->cache_list)){
1866 hlist_del_init(&h->cache_list); 1931 hlist_del_init_rcu(&h->cache_list);
1867 cd->entries--; 1932 cd->entries--;
1868 write_unlock(&cd->hash_lock); 1933 write_unlock(&cd->hash_lock);
1869 cache_put(h, cd); 1934 cache_put(h, cd);