aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fs/nfs/dir.c43
-rw-r--r--include/linux/nfs_fs.h1
2 files changed, 42 insertions, 2 deletions
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c
index 2bfbde0f7176..1b5f38f48dab 100644
--- a/fs/nfs/dir.c
+++ b/fs/nfs/dir.c
@@ -2079,7 +2079,7 @@ MODULE_PARM_DESC(nfs_access_max_cachesize, "NFS access maximum total cache lengt
2079static void nfs_access_free_entry(struct nfs_access_entry *entry) 2079static void nfs_access_free_entry(struct nfs_access_entry *entry)
2080{ 2080{
2081 put_rpccred(entry->cred); 2081 put_rpccred(entry->cred);
2082 kfree(entry); 2082 kfree_rcu(entry, rcu_head);
2083 smp_mb__before_atomic(); 2083 smp_mb__before_atomic();
2084 atomic_long_dec(&nfs_access_nr_entries); 2084 atomic_long_dec(&nfs_access_nr_entries);
2085 smp_mb__after_atomic(); 2085 smp_mb__after_atomic();
@@ -2257,6 +2257,38 @@ out_zap:
2257 return -ENOENT; 2257 return -ENOENT;
2258} 2258}
2259 2259
2260static int nfs_access_get_cached_rcu(struct inode *inode, struct rpc_cred *cred, struct nfs_access_entry *res)
2261{
2262 /* Only check the most recently returned cache entry,
2263 * but do it without locking.
2264 */
2265 struct nfs_inode *nfsi = NFS_I(inode);
2266 struct nfs_access_entry *cache;
2267 int err = -ECHILD;
2268 struct list_head *lh;
2269
2270 rcu_read_lock();
2271 if (nfsi->cache_validity & NFS_INO_INVALID_ACCESS)
2272 goto out;
2273 lh = rcu_dereference(nfsi->access_cache_entry_lru.prev);
2274 cache = list_entry(lh, struct nfs_access_entry, lru);
2275 if (lh == &nfsi->access_cache_entry_lru ||
2276 cred != cache->cred)
2277 cache = NULL;
2278 if (cache == NULL)
2279 goto out;
2280 if (!nfs_have_delegated_attributes(inode) &&
2281 !time_in_range_open(jiffies, cache->jiffies, cache->jiffies + nfsi->attrtimeo))
2282 goto out;
2283 res->jiffies = cache->jiffies;
2284 res->cred = cache->cred;
2285 res->mask = cache->mask;
2286 err = 0;
2287out:
2288 rcu_read_unlock();
2289 return err;
2290}
2291
2260static void nfs_access_add_rbtree(struct inode *inode, struct nfs_access_entry *set) 2292static void nfs_access_add_rbtree(struct inode *inode, struct nfs_access_entry *set)
2261{ 2293{
2262 struct nfs_inode *nfsi = NFS_I(inode); 2294 struct nfs_inode *nfsi = NFS_I(inode);
@@ -2300,6 +2332,11 @@ void nfs_access_add_cache(struct inode *inode, struct nfs_access_entry *set)
2300 cache->cred = get_rpccred(set->cred); 2332 cache->cred = get_rpccred(set->cred);
2301 cache->mask = set->mask; 2333 cache->mask = set->mask;
2302 2334
2335 /* The above field assignments must be visible
2336 * before this item appears on the lru. We cannot easily
2337 * use rcu_assign_pointer, so just force the memory barrier.
2338 */
2339 smp_wmb();
2303 nfs_access_add_rbtree(inode, cache); 2340 nfs_access_add_rbtree(inode, cache);
2304 2341
2305 /* Update accounting */ 2342 /* Update accounting */
@@ -2339,7 +2376,9 @@ static int nfs_do_access(struct inode *inode, struct rpc_cred *cred, int mask)
2339 2376
2340 trace_nfs_access_enter(inode); 2377 trace_nfs_access_enter(inode);
2341 2378
2342 status = nfs_access_get_cached(inode, cred, &cache); 2379 status = nfs_access_get_cached_rcu(inode, cred, &cache);
2380 if (status != 0)
2381 status = nfs_access_get_cached(inode, cred, &cache);
2343 if (status == 0) 2382 if (status == 0)
2344 goto out_cached; 2383 goto out_cached;
2345 2384
diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h
index 60cd9e377926..5180a7ededec 100644
--- a/include/linux/nfs_fs.h
+++ b/include/linux/nfs_fs.h
@@ -52,6 +52,7 @@ struct nfs_access_entry {
52 unsigned long jiffies; 52 unsigned long jiffies;
53 struct rpc_cred * cred; 53 struct rpc_cred * cred;
54 int mask; 54 int mask;
55 struct rcu_head rcu_head;
55}; 56};
56 57
57struct nfs_lockowner { 58struct nfs_lockowner {