diff options
-rw-r--r-- | fs/nfs/inode.c | 2 | ||||
-rw-r--r-- | fs/nfs/pnfs.c | 130 | ||||
-rw-r--r-- | fs/nfs/pnfs.h | 8 |
3 files changed, 96 insertions, 44 deletions
diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index c7782b278e8b..790b786e1ae1 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c | |||
@@ -1410,9 +1410,9 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr) | |||
1410 | */ | 1410 | */ |
1411 | void nfs4_evict_inode(struct inode *inode) | 1411 | void nfs4_evict_inode(struct inode *inode) |
1412 | { | 1412 | { |
1413 | pnfs_destroy_layout(NFS_I(inode)); | ||
1413 | truncate_inode_pages(&inode->i_data, 0); | 1414 | truncate_inode_pages(&inode->i_data, 0); |
1414 | end_writeback(inode); | 1415 | end_writeback(inode); |
1415 | pnfs_destroy_layout(NFS_I(inode)); | ||
1416 | /* If we are holding a delegation, return it! */ | 1416 | /* If we are holding a delegation, return it! */ |
1417 | nfs_inode_return_delegation_noreclaim(inode); | 1417 | nfs_inode_return_delegation_noreclaim(inode); |
1418 | /* First call standard NFS clear_inode() code */ | 1418 | /* First call standard NFS clear_inode() code */ |
diff --git a/fs/nfs/pnfs.c b/fs/nfs/pnfs.c index 08313f536b45..212cbc22c59d 100644 --- a/fs/nfs/pnfs.c +++ b/fs/nfs/pnfs.c | |||
@@ -211,68 +211,109 @@ static void | |||
211 | init_lseg(struct pnfs_layout_hdr *lo, struct pnfs_layout_segment *lseg) | 211 | init_lseg(struct pnfs_layout_hdr *lo, struct pnfs_layout_segment *lseg) |
212 | { | 212 | { |
213 | INIT_LIST_HEAD(&lseg->pls_list); | 213 | INIT_LIST_HEAD(&lseg->pls_list); |
214 | kref_init(&lseg->pls_refcount); | 214 | atomic_set(&lseg->pls_refcount, 1); |
215 | smp_mb(); | ||
216 | set_bit(NFS_LSEG_VALID, &lseg->pls_flags); | ||
215 | lseg->pls_layout = lo; | 217 | lseg->pls_layout = lo; |
216 | } | 218 | } |
217 | 219 | ||
218 | /* Called without i_lock held, as the free_lseg call may sleep */ | 220 | static void free_lseg(struct pnfs_layout_segment *lseg) |
219 | static void | ||
220 | destroy_lseg(struct kref *kref) | ||
221 | { | 221 | { |
222 | struct pnfs_layout_segment *lseg = | ||
223 | container_of(kref, struct pnfs_layout_segment, pls_refcount); | ||
224 | struct inode *ino = lseg->pls_layout->plh_inode; | 222 | struct inode *ino = lseg->pls_layout->plh_inode; |
225 | 223 | ||
226 | dprintk("--> %s\n", __func__); | ||
227 | NFS_SERVER(ino)->pnfs_curr_ld->free_lseg(lseg); | 224 | NFS_SERVER(ino)->pnfs_curr_ld->free_lseg(lseg); |
228 | /* Matched by get_layout_hdr in pnfs_insert_layout */ | 225 | /* Matched by get_layout_hdr in pnfs_insert_layout */ |
229 | put_layout_hdr(ino); | 226 | put_layout_hdr(ino); |
230 | } | 227 | } |
231 | 228 | ||
232 | static void | 229 | /* The use of tmp_list is necessary because pnfs_curr_ld->free_lseg |
233 | put_lseg(struct pnfs_layout_segment *lseg) | 230 | * could sleep, so must be called outside of the lock. |
231 | * Returns 1 if object was removed, otherwise return 0. | ||
232 | */ | ||
233 | static int | ||
234 | put_lseg_locked(struct pnfs_layout_segment *lseg, | ||
235 | struct list_head *tmp_list) | ||
234 | { | 236 | { |
235 | if (!lseg) | 237 | dprintk("%s: lseg %p ref %d valid %d\n", __func__, lseg, |
236 | return; | 238 | atomic_read(&lseg->pls_refcount), |
239 | test_bit(NFS_LSEG_VALID, &lseg->pls_flags)); | ||
240 | if (atomic_dec_and_test(&lseg->pls_refcount)) { | ||
241 | struct inode *ino = lseg->pls_layout->plh_inode; | ||
237 | 242 | ||
238 | dprintk("%s: lseg %p ref %d\n", __func__, lseg, | 243 | BUG_ON(test_bit(NFS_LSEG_VALID, &lseg->pls_flags)); |
239 | atomic_read(&lseg->pls_refcount.refcount)); | 244 | list_del(&lseg->pls_list); |
240 | kref_put(&lseg->pls_refcount, destroy_lseg); | 245 | if (list_empty(&lseg->pls_layout->plh_segs)) { |
246 | struct nfs_client *clp; | ||
247 | |||
248 | clp = NFS_SERVER(ino)->nfs_client; | ||
249 | spin_lock(&clp->cl_lock); | ||
250 | /* List does not take a reference, so no need for put here */ | ||
251 | list_del_init(&lseg->pls_layout->plh_layouts); | ||
252 | spin_unlock(&clp->cl_lock); | ||
253 | } | ||
254 | list_add(&lseg->pls_list, tmp_list); | ||
255 | return 1; | ||
256 | } | ||
257 | return 0; | ||
241 | } | 258 | } |
242 | 259 | ||
243 | static void | 260 | static bool |
244 | pnfs_clear_lseg_list(struct pnfs_layout_hdr *lo, struct list_head *tmp_list) | 261 | should_free_lseg(u32 lseg_iomode, u32 recall_iomode) |
245 | { | 262 | { |
246 | struct pnfs_layout_segment *lseg, *next; | 263 | return (recall_iomode == IOMODE_ANY || |
247 | struct nfs_client *clp; | 264 | lseg_iomode == recall_iomode); |
265 | } | ||
248 | 266 | ||
249 | dprintk("%s:Begin lo %p\n", __func__, lo); | 267 | /* Returns 1 if lseg is removed from list, 0 otherwise */ |
268 | static int mark_lseg_invalid(struct pnfs_layout_segment *lseg, | ||
269 | struct list_head *tmp_list) | ||
270 | { | ||
271 | int rv = 0; | ||
250 | 272 | ||
251 | assert_spin_locked(&lo->plh_inode->i_lock); | 273 | if (test_and_clear_bit(NFS_LSEG_VALID, &lseg->pls_flags)) { |
252 | list_for_each_entry_safe(lseg, next, &lo->plh_segs, pls_list) { | 274 | /* Remove the reference keeping the lseg in the |
253 | dprintk("%s: freeing lseg %p\n", __func__, lseg); | 275 | * list. It will now be removed when all |
254 | list_move(&lseg->pls_list, tmp_list); | 276 | * outstanding io is finished. |
277 | */ | ||
278 | rv = put_lseg_locked(lseg, tmp_list); | ||
255 | } | 279 | } |
256 | clp = NFS_SERVER(lo->plh_inode)->nfs_client; | 280 | return rv; |
257 | spin_lock(&clp->cl_lock); | 281 | } |
258 | /* List does not take a reference, so no need for put here */ | ||
259 | list_del_init(&lo->plh_layouts); | ||
260 | spin_unlock(&clp->cl_lock); | ||
261 | 282 | ||
262 | dprintk("%s:Return\n", __func__); | 283 | /* Returns count of number of matching invalid lsegs remaining in list |
284 | * after call. | ||
285 | */ | ||
286 | static int | ||
287 | mark_matching_lsegs_invalid(struct pnfs_layout_hdr *lo, | ||
288 | struct list_head *tmp_list, | ||
289 | u32 iomode) | ||
290 | { | ||
291 | struct pnfs_layout_segment *lseg, *next; | ||
292 | int invalid = 0, removed = 0; | ||
293 | |||
294 | dprintk("%s:Begin lo %p\n", __func__, lo); | ||
295 | |||
296 | list_for_each_entry_safe(lseg, next, &lo->plh_segs, pls_list) | ||
297 | if (should_free_lseg(lseg->pls_range.iomode, iomode)) { | ||
298 | dprintk("%s: freeing lseg %p iomode %d " | ||
299 | "offset %llu length %llu\n", __func__, | ||
300 | lseg, lseg->pls_range.iomode, lseg->pls_range.offset, | ||
301 | lseg->pls_range.length); | ||
302 | invalid++; | ||
303 | removed += mark_lseg_invalid(lseg, tmp_list); | ||
304 | } | ||
305 | dprintk("%s:Return %i\n", __func__, invalid - removed); | ||
306 | return invalid - removed; | ||
263 | } | 307 | } |
264 | 308 | ||
265 | static void | 309 | static void |
266 | pnfs_free_lseg_list(struct list_head *tmp_list) | 310 | pnfs_free_lseg_list(struct list_head *free_me) |
267 | { | 311 | { |
268 | struct pnfs_layout_segment *lseg; | 312 | struct pnfs_layout_segment *lseg, *tmp; |
269 | 313 | ||
270 | while (!list_empty(tmp_list)) { | 314 | list_for_each_entry_safe(lseg, tmp, free_me, pls_list) { |
271 | lseg = list_entry(tmp_list->next, struct pnfs_layout_segment, | ||
272 | pls_list); | ||
273 | dprintk("%s calling put_lseg on %p\n", __func__, lseg); | ||
274 | list_del(&lseg->pls_list); | 315 | list_del(&lseg->pls_list); |
275 | put_lseg(lseg); | 316 | free_lseg(lseg); |
276 | } | 317 | } |
277 | } | 318 | } |
278 | 319 | ||
@@ -285,7 +326,8 @@ pnfs_destroy_layout(struct nfs_inode *nfsi) | |||
285 | spin_lock(&nfsi->vfs_inode.i_lock); | 326 | spin_lock(&nfsi->vfs_inode.i_lock); |
286 | lo = nfsi->layout; | 327 | lo = nfsi->layout; |
287 | if (lo) { | 328 | if (lo) { |
288 | pnfs_clear_lseg_list(lo, &tmp_list); | 329 | set_bit(NFS_LAYOUT_DESTROYED, &nfsi->layout->plh_flags); |
330 | mark_matching_lsegs_invalid(lo, &tmp_list, IOMODE_ANY); | ||
289 | /* Matched by refcount set to 1 in alloc_init_layout_hdr */ | 331 | /* Matched by refcount set to 1 in alloc_init_layout_hdr */ |
290 | put_layout_hdr_locked(lo); | 332 | put_layout_hdr_locked(lo); |
291 | } | 333 | } |
@@ -477,9 +519,12 @@ pnfs_find_alloc_layout(struct inode *ino) | |||
477 | dprintk("%s Begin ino=%p layout=%p\n", __func__, ino, nfsi->layout); | 519 | dprintk("%s Begin ino=%p layout=%p\n", __func__, ino, nfsi->layout); |
478 | 520 | ||
479 | assert_spin_locked(&ino->i_lock); | 521 | assert_spin_locked(&ino->i_lock); |
480 | if (nfsi->layout) | 522 | if (nfsi->layout) { |
481 | return nfsi->layout; | 523 | if (test_bit(NFS_LAYOUT_DESTROYED, &nfsi->layout->plh_flags)) |
482 | 524 | return NULL; | |
525 | else | ||
526 | return nfsi->layout; | ||
527 | } | ||
483 | spin_unlock(&ino->i_lock); | 528 | spin_unlock(&ino->i_lock); |
484 | new = alloc_init_layout_hdr(ino); | 529 | new = alloc_init_layout_hdr(ino); |
485 | spin_lock(&ino->i_lock); | 530 | spin_lock(&ino->i_lock); |
@@ -520,7 +565,8 @@ pnfs_has_layout(struct pnfs_layout_hdr *lo, u32 iomode) | |||
520 | 565 | ||
521 | assert_spin_locked(&lo->plh_inode->i_lock); | 566 | assert_spin_locked(&lo->plh_inode->i_lock); |
522 | list_for_each_entry(lseg, &lo->plh_segs, pls_list) { | 567 | list_for_each_entry(lseg, &lo->plh_segs, pls_list) { |
523 | if (is_matching_lseg(lseg, iomode)) { | 568 | if (test_bit(NFS_LSEG_VALID, &lseg->pls_flags) && |
569 | is_matching_lseg(lseg, iomode)) { | ||
524 | ret = lseg; | 570 | ret = lseg; |
525 | break; | 571 | break; |
526 | } | 572 | } |
@@ -529,7 +575,7 @@ pnfs_has_layout(struct pnfs_layout_hdr *lo, u32 iomode) | |||
529 | } | 575 | } |
530 | 576 | ||
531 | dprintk("%s:Return lseg %p ref %d\n", | 577 | dprintk("%s:Return lseg %p ref %d\n", |
532 | __func__, ret, ret ? atomic_read(&ret->pls_refcount.refcount) : 0); | 578 | __func__, ret, ret ? atomic_read(&ret->pls_refcount) : 0); |
533 | return ret; | 579 | return ret; |
534 | } | 580 | } |
535 | 581 | ||
diff --git a/fs/nfs/pnfs.h b/fs/nfs/pnfs.h index 10937203d236..787253e6fca3 100644 --- a/fs/nfs/pnfs.h +++ b/fs/nfs/pnfs.h | |||
@@ -30,10 +30,15 @@ | |||
30 | #ifndef FS_NFS_PNFS_H | 30 | #ifndef FS_NFS_PNFS_H |
31 | #define FS_NFS_PNFS_H | 31 | #define FS_NFS_PNFS_H |
32 | 32 | ||
33 | enum { | ||
34 | NFS_LSEG_VALID = 0, /* cleared when lseg is recalled/returned */ | ||
35 | }; | ||
36 | |||
33 | struct pnfs_layout_segment { | 37 | struct pnfs_layout_segment { |
34 | struct list_head pls_list; | 38 | struct list_head pls_list; |
35 | struct pnfs_layout_range pls_range; | 39 | struct pnfs_layout_range pls_range; |
36 | struct kref pls_refcount; | 40 | atomic_t pls_refcount; |
41 | unsigned long pls_flags; | ||
37 | struct pnfs_layout_hdr *pls_layout; | 42 | struct pnfs_layout_hdr *pls_layout; |
38 | }; | 43 | }; |
39 | 44 | ||
@@ -44,6 +49,7 @@ struct pnfs_layout_segment { | |||
44 | enum { | 49 | enum { |
45 | NFS_LAYOUT_RO_FAILED = 0, /* get ro layout failed stop trying */ | 50 | NFS_LAYOUT_RO_FAILED = 0, /* get ro layout failed stop trying */ |
46 | NFS_LAYOUT_RW_FAILED, /* get rw layout failed stop trying */ | 51 | NFS_LAYOUT_RW_FAILED, /* get rw layout failed stop trying */ |
52 | NFS_LAYOUT_DESTROYED, /* no new use of layout allowed */ | ||
47 | }; | 53 | }; |
48 | 54 | ||
49 | /* Per-layout driver specific registration structure */ | 55 | /* Per-layout driver specific registration structure */ |