aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFred Isaman <iisaman@netapp.com>2011-01-06 06:36:23 -0500
committerTrond Myklebust <Trond.Myklebust@netapp.com>2011-01-06 14:46:31 -0500
commit4541d16c024ce40a0781e03c185ecdfe34aec46f (patch)
tree53574211b3de874869b09644b8de872ccf2d4fe1
parentfd6002e9b8a93220d5f53b93d9624caf73cdc8a2 (diff)
pnfs: change how lsegs are removed from layout list
This is to prepare the way for sensible io draining. Instead of just removing the lseg from the list, we instead clear the VALID flag (preventing new io from grabbing references to the lseg) and remove the reference holding it in the list. Thus the lseg will be removed once any io in progress completes and any references still held are dropped. Signed-off-by: Fred Isaman <iisaman@netapp.com> Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
-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 */