aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fs/nfs/inode.c2
-rw-r--r--fs/nfs/pnfs.c130
-rw-r--r--fs/nfs/pnfs.h8
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 */
1411void nfs4_evict_inode(struct inode *inode) 1411void 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
211init_lseg(struct pnfs_layout_hdr *lo, struct pnfs_layout_segment *lseg) 211init_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 */ 220static void free_lseg(struct pnfs_layout_segment *lseg)
219static void
220destroy_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
232static void 229/* The use of tmp_list is necessary because pnfs_curr_ld->free_lseg
233put_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 */
233static int
234put_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
243static void 260static bool
244pnfs_clear_lseg_list(struct pnfs_layout_hdr *lo, struct list_head *tmp_list) 261should_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 */
268static 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 */
286static int
287mark_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
265static void 309static void
266pnfs_free_lseg_list(struct list_head *tmp_list) 310pnfs_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
33enum {
34 NFS_LSEG_VALID = 0, /* cleared when lseg is recalled/returned */
35};
36
33struct pnfs_layout_segment { 37struct 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 {
44enum { 49enum {
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 */