diff options
Diffstat (limited to 'fs/gfs2/lops.c')
-rw-r--r-- | fs/gfs2/lops.c | 33 |
1 files changed, 24 insertions, 9 deletions
diff --git a/fs/gfs2/lops.c b/fs/gfs2/lops.c index b4f0a6a3ba59..2fd61853ba63 100644 --- a/fs/gfs2/lops.c +++ b/fs/gfs2/lops.c @@ -662,19 +662,34 @@ static void revoke_lo_before_commit(struct gfs2_sbd *sdp, struct gfs2_trans *tr) static void revoke_lo_after_commit(struct gfs2_sbd *sdp, struct gfs2_trans *tr) { struct list_head *head = &sdp->sd_log_le_revoke; - struct gfs2_bufdata *bd; - struct gfs2_glock *gl; + struct gfs2_bufdata *bd, *tmp; - while (!list_empty(head)) { - bd = list_entry(head->next, struct gfs2_bufdata, bd_list); - list_del_init(&bd->bd_list); - gl = bd->bd_gl; - if (atomic_dec_return(&gl->gl_revokes) == 0) { - clear_bit(GLF_LFLUSH, &gl->gl_flags); - gfs2_glock_queue_put(gl); + /* + * Glocks can be referenced repeatedly on the revoke list, but the list + * only holds one reference. All glocks on the list will have the + * GLF_REVOKES flag set initially. + */ + + list_for_each_entry_safe(bd, tmp, head, bd_list) { + struct gfs2_glock *gl = bd->bd_gl; + + if (test_bit(GLF_REVOKES, &gl->gl_flags)) { + /* Keep each glock on the list exactly once. */ + clear_bit(GLF_REVOKES, &gl->gl_flags); + continue; } + list_del(&bd->bd_list); + kmem_cache_free(gfs2_bufdata_cachep, bd); + } + list_for_each_entry_safe(bd, tmp, head, bd_list) { + struct gfs2_glock *gl = bd->bd_gl; + + list_del(&bd->bd_list); kmem_cache_free(gfs2_bufdata_cachep, bd); + clear_bit(GLF_LFLUSH, &gl->gl_flags); + gfs2_glock_queue_put(gl); } + /* the list is empty now */ } static void revoke_lo_before_scan(struct gfs2_jdesc *jd, |