diff options
author | Fred Isaman <iisaman@netapp.com> | 2011-01-06 06:36:23 -0500 |
---|---|---|
committer | Trond Myklebust <Trond.Myklebust@netapp.com> | 2011-01-06 14:46:31 -0500 |
commit | 4541d16c024ce40a0781e03c185ecdfe34aec46f (patch) | |
tree | 53574211b3de874869b09644b8de872ccf2d4fe1 /fs/nfs/pnfs.c | |
parent | fd6002e9b8a93220d5f53b93d9624caf73cdc8a2 (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>
Diffstat (limited to 'fs/nfs/pnfs.c')
-rw-r--r-- | fs/nfs/pnfs.c | 130 |
1 files changed, 88 insertions, 42 deletions
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 | ||