diff options
-rw-r--r-- | fs/nfs/dir.c | 43 | ||||
-rw-r--r-- | include/linux/nfs_fs.h | 1 |
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 | |||
2079 | static void nfs_access_free_entry(struct nfs_access_entry *entry) | 2079 | static 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 | ||
2260 | static 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; | ||
2287 | out: | ||
2288 | rcu_read_unlock(); | ||
2289 | return err; | ||
2290 | } | ||
2291 | |||
2260 | static void nfs_access_add_rbtree(struct inode *inode, struct nfs_access_entry *set) | 2292 | static 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 | ||
57 | struct nfs_lockowner { | 58 | struct nfs_lockowner { |