int gfs2_releasepage(struct page *page, gfp_t gfp_mask) { struct address_space *mapping = page->mapping; struct gfs2_sbd *sdp = gfs2_mapping2sbd(mapping); struct buffer_head *bh, *head; struct gfs2_bufdata *bd; if (!page_has_buffers(page)) return 0; gfs2_log_lock(sdp); spin_lock(&sdp->sd_ail_lock); head = bh = page_buffers(page); do { if (atomic_read(&bh->b_count)) goto cannot_release; bd = bh->b_private; if (bd && bd->bd_ail) goto cannot_release; if (buffer_pinned(bh) || buffer_dirty(bh)) goto not_possible; bh = bh->b_this_page; } while(bh != head); spin_unlock(&sdp->sd_ail_lock); gfs2_log_unlock(sdp); head = bh = page_buffers(page); do { gfs2_log_lock(sdp); bd = bh->b_private; if (bd) { gfs2_assert_warn(sdp, bd->bd_bh == bh); gfs2_assert_warn(sdp, list_empty(&bd->bd_list_tr)); if (!list_empty(&bd->bd_le.le_list)) { if (!buffer_pinned(bh)) list_del_init(&bd->bd_le.le_list); else bd = NULL; } if (bd) bd->bd_bh = NULL; bh->b_private = NULL; } gfs2_log_unlock(sdp); if (bd) kmem_cache_free(gfs2_bufdata_cachep, bd); bh = bh->b_this_page; } while (bh != head); return try_to_free_buffers(page); not_possible: /* Should never happen */ WARN_ON(buffer_dirty(bh)); WARN_ON(buffer_pinned(bh)); cannot_release: spin_unlock(&sdp->sd_ail_lock); gfs2_log_unlock(sdp); return 0; }
int gfs2_releasepage(struct page *page, gfp_t gfp_mask) { struct inode *aspace = page->mapping->host; struct gfs2_sbd *sdp = aspace->i_sb->s_fs_info; struct buffer_head *bh, *head; struct gfs2_bufdata *bd; if (!page_has_buffers(page)) return 0; gfs2_log_lock(sdp); head = bh = page_buffers(page); do { if (atomic_read(&bh->b_count)) goto cannot_release; bd = bh->b_private; if (bd && bd->bd_ail) goto cannot_release; gfs2_assert_warn(sdp, !buffer_pinned(bh)); gfs2_assert_warn(sdp, !buffer_dirty(bh)); bh = bh->b_this_page; } while(bh != head); gfs2_log_unlock(sdp); head = bh = page_buffers(page); do { gfs2_log_lock(sdp); bd = bh->b_private; if (bd) { gfs2_assert_warn(sdp, bd->bd_bh == bh); gfs2_assert_warn(sdp, list_empty(&bd->bd_list_tr)); if (!list_empty(&bd->bd_le.le_list)) { if (!buffer_pinned(bh)) list_del_init(&bd->bd_le.le_list); else bd = NULL; } if (bd) bd->bd_bh = NULL; bh->b_private = NULL; } gfs2_log_unlock(sdp); if (bd) kmem_cache_free(gfs2_bufdata_cachep, bd); bh = bh->b_this_page; } while (bh != head); return try_to_free_buffers(page); cannot_release: gfs2_log_unlock(sdp); return 0; }
static void __gfs2_ail_flush(struct gfs2_glock *gl, bool fsync) { struct gfs2_sbd *sdp = gl->gl_sbd; struct list_head *head = &gl->gl_ail_list; struct gfs2_bufdata *bd, *tmp; struct buffer_head *bh; const unsigned long b_state = (1UL << BH_Dirty)|(1UL << BH_Pinned)|(1UL << BH_Lock); sector_t blocknr; gfs2_log_lock(sdp); spin_lock(&sdp->sd_ail_lock); list_for_each_entry_safe(bd, tmp, head, bd_ail_gl_list) { bh = bd->bd_bh; if (bh->b_state & b_state) { if (fsync) continue; gfs2_ail_error(gl, bh); } blocknr = bh->b_blocknr; bh->b_private = NULL; gfs2_remove_from_ail(bd); /* drops ref on bh */ bd->bd_bh = NULL; bd->bd_blkno = blocknr; gfs2_trans_add_revoke(sdp, bd); }
static void __gfs2_ail_flush(struct gfs2_glock *gl, bool fsync, unsigned int nr_revokes) { struct gfs2_sbd *sdp = gl->gl_sbd; struct list_head *head = &gl->gl_ail_list; struct gfs2_bufdata *bd, *tmp; struct buffer_head *bh; const unsigned long b_state = (1UL << BH_Dirty)|(1UL << BH_Pinned)|(1UL << BH_Lock); gfs2_log_lock(sdp); spin_lock(&sdp->sd_ail_lock); list_for_each_entry_safe_reverse(bd, tmp, head, bd_ail_gl_list) { if (nr_revokes == 0) break; bh = bd->bd_bh; if (bh->b_state & b_state) { if (fsync) continue; gfs2_ail_error(gl, bh); } gfs2_trans_add_revoke(sdp, bd); nr_revokes--; } GLOCK_BUG_ON(gl, !fsync && atomic_read(&gl->gl_ail_count)); spin_unlock(&sdp->sd_ail_lock); gfs2_log_unlock(sdp); }
static void gfs2_ail_empty_gl(struct gfs2_glock *gl) { struct gfs2_sbd *sdp = gl->gl_sbd; unsigned int blocks; struct list_head *head = &gl->gl_ail_list; struct gfs2_bufdata *bd; struct buffer_head *bh; u64 blkno; int error; blocks = atomic_read(&gl->gl_ail_count); if (!blocks) return; error = gfs2_trans_begin(sdp, 0, blocks); if (gfs2_assert_withdraw(sdp, !error)) return; gfs2_log_lock(sdp); while (!list_empty(head)) { bd = list_entry(head->next, struct gfs2_bufdata, bd_ail_gl_list); bh = bd->bd_bh; blkno = bh->b_blocknr; gfs2_assert_withdraw(sdp, !buffer_busy(bh)); bd->bd_ail = NULL; list_del(&bd->bd_ail_st_list); list_del(&bd->bd_ail_gl_list); atomic_dec(&gl->gl_ail_count); brelse(bh); gfs2_log_unlock(sdp); gfs2_trans_add_revoke(sdp, blkno); gfs2_log_lock(sdp); } gfs2_assert_withdraw(sdp, !atomic_read(&gl->gl_ail_count)); gfs2_log_unlock(sdp); gfs2_trans_end(sdp); gfs2_log_flush(sdp, NULL); }
int gfs2_releasepage(struct page *page, gfp_t gfp_mask) { struct inode *aspace = page->mapping->host; struct gfs2_sbd *sdp = aspace->i_sb->s_fs_info; struct buffer_head *bh, *head; struct gfs2_bufdata *bd; unsigned long t = jiffies + gfs2_tune_get(sdp, gt_stall_secs) * HZ; if (!page_has_buffers(page)) goto out; head = bh = page_buffers(page); do { while (atomic_read(&bh->b_count)) { if (!atomic_read(&aspace->i_writecount)) return 0; if (!(gfp_mask & __GFP_WAIT)) return 0; if (time_after_eq(jiffies, t)) { stuck_releasepage(bh); /* should we withdraw here? */ return 0; } yield(); } gfs2_assert_warn(sdp, !buffer_pinned(bh)); gfs2_assert_warn(sdp, !buffer_dirty(bh)); gfs2_log_lock(sdp); bd = bh->b_private; if (bd) { gfs2_assert_warn(sdp, bd->bd_bh == bh); gfs2_assert_warn(sdp, list_empty(&bd->bd_list_tr)); gfs2_assert_warn(sdp, !bd->bd_ail); bd->bd_bh = NULL; if (!list_empty(&bd->bd_le.le_list)) bd = NULL; bh->b_private = NULL; } gfs2_log_unlock(sdp); if (bd) kmem_cache_free(gfs2_bufdata_cachep, bd); bh = bh->b_this_page; } while (bh != head); out: return try_to_free_buffers(page); }
static void gfs2_ail_empty_gl(struct gfs2_glock *gl) { struct gfs2_sbd *sdp = gl->gl_sbd; struct list_head *head = &gl->gl_ail_list; struct gfs2_bufdata *bd; struct buffer_head *bh; struct gfs2_trans tr; memset(&tr, 0, sizeof(tr)); tr.tr_revokes = atomic_read(&gl->gl_ail_count); if (!tr.tr_revokes) return; /* A shortened, inline version of gfs2_trans_begin() */ tr.tr_reserved = 1 + gfs2_struct2blk(sdp, tr.tr_revokes, sizeof(u64)); tr.tr_ip = (unsigned long)__builtin_return_address(0); INIT_LIST_HEAD(&tr.tr_list_buf); gfs2_log_reserve(sdp, tr.tr_reserved); BUG_ON(current->journal_info); current->journal_info = &tr; spin_lock(&sdp->sd_ail_lock); while (!list_empty(head)) { bd = list_entry(head->next, struct gfs2_bufdata, bd_ail_gl_list); bh = bd->bd_bh; gfs2_remove_from_ail(bd); spin_unlock(&sdp->sd_ail_lock); bd->bd_bh = NULL; bh->b_private = NULL; bd->bd_blkno = bh->b_blocknr; gfs2_log_lock(sdp); gfs2_assert_withdraw(sdp, !buffer_busy(bh)); gfs2_trans_add_revoke(sdp, bd); gfs2_log_unlock(sdp); spin_lock(&sdp->sd_ail_lock); } gfs2_assert_withdraw(sdp, !atomic_read(&gl->gl_ail_count)); spin_unlock(&sdp->sd_ail_lock); gfs2_trans_end(sdp); gfs2_log_flush(sdp, NULL); }
static void __gfs2_ail_flush(struct gfs2_glock *gl, bool fsync) { struct gfs2_sbd *sdp = gl->gl_sbd; struct list_head *head = &gl->gl_ail_list; struct gfs2_bufdata *bd, *tmp; struct buffer_head *bh; const unsigned long b_state = (1UL << BH_Dirty)|(1UL << BH_Pinned)|(1UL << BH_Lock); gfs2_log_lock(sdp); spin_lock(&sdp->sd_ail_lock); list_for_each_entry_safe(bd, tmp, head, bd_ail_gl_list) { bh = bd->bd_bh; if (bh->b_state & b_state) { if (fsync) continue; gfs2_ail_error(gl, bh); } gfs2_trans_add_revoke(sdp, bd); }
void gfs2_meta_wipe(struct gfs2_inode *ip, u64 bstart, u32 blen) { struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); struct buffer_head *bh; while (blen) { bh = gfs2_getbuf(ip->i_gl, bstart, NO_CREATE); if (bh) { lock_buffer(bh); gfs2_log_lock(sdp); gfs2_remove_from_journal(bh, current->journal_info, 1); gfs2_log_unlock(sdp); unlock_buffer(bh); brelse(bh); } bstart++; blen--; } }
static void gfs2_discard(struct gfs2_sbd *sdp, struct buffer_head *bh) { struct gfs2_bufdata *bd; lock_buffer(bh); gfs2_log_lock(sdp); clear_buffer_dirty(bh); bd = bh->b_private; if (bd) { if (!list_empty(&bd->bd_le.le_list) && !buffer_pinned(bh)) list_del_init(&bd->bd_le.le_list); else gfs2_remove_from_journal(bh, current->journal_info, 0); } bh->b_bdev = NULL; clear_buffer_mapped(bh); clear_buffer_req(bh); clear_buffer_new(bh); gfs2_log_unlock(sdp); unlock_buffer(bh); }
static void discard_buffer(struct gfs2_sbd *sdp, struct buffer_head *bh) { struct gfs2_bufdata *bd; gfs2_log_lock(sdp); bd = bh->b_private; if (bd) { bd->bd_bh = NULL; bh->b_private = NULL; } gfs2_log_unlock(sdp); lock_buffer(bh); clear_buffer_dirty(bh); bh->b_bdev = NULL; clear_buffer_mapped(bh); clear_buffer_req(bh); clear_buffer_new(bh); clear_buffer_delay(bh); unlock_buffer(bh); }
int gfs2_logd(void *data) { struct gfs2_sbd *sdp = data; struct gfs2_holder ji_gh; unsigned long t; int need_flush; while (!kthread_should_stop()) { /* Advance the log tail */ t = sdp->sd_log_flush_time + gfs2_tune_get(sdp, gt_log_flush_secs) * HZ; gfs2_ail1_empty(sdp, DIO_ALL); gfs2_log_lock(sdp); need_flush = sdp->sd_log_num_buf > gfs2_tune_get(sdp, gt_incore_log_blocks); gfs2_log_unlock(sdp); if (need_flush || time_after_eq(jiffies, t)) { gfs2_log_flush(sdp, NULL); sdp->sd_log_flush_time = jiffies; } /* Check for latest journal index */ t = sdp->sd_jindex_refresh_time + gfs2_tune_get(sdp, gt_jindex_refresh_secs) * HZ; if (time_after_eq(jiffies, t)) { if (!gfs2_jindex_hold(sdp, &ji_gh)) gfs2_glock_dq_uninit(&ji_gh); sdp->sd_jindex_refresh_time = jiffies; } t = gfs2_tune_get(sdp, gt_logd_secs) * HZ; schedule_timeout_interruptible(t); } return 0; }