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; }
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 stuck_releasepage(struct buffer_head *bh) { struct inode *inode = bh->b_page->mapping->host; struct gfs2_sbd *sdp = inode->i_sb->s_fs_info; struct gfs2_bufdata *bd = bh->b_private; struct gfs2_glock *gl; static unsigned limit = 0; if (limit > 3) return; limit++; fs_warn(sdp, "stuck in gfs2_releasepage() %p\n", inode); fs_warn(sdp, "blkno = %llu, bh->b_count = %d\n", (unsigned long long)bh->b_blocknr, atomic_read(&bh->b_count)); fs_warn(sdp, "pinned = %u\n", buffer_pinned(bh)); fs_warn(sdp, "bh->b_private = %s\n", (bd) ? "!NULL" : "NULL"); if (!bd) return; gl = bd->bd_gl; fs_warn(sdp, "gl = (%u, %llu)\n", gl->gl_name.ln_type, (unsigned long long)gl->gl_name.ln_number); fs_warn(sdp, "bd_list_tr = %s, bd_le.le_list = %s\n", (list_empty(&bd->bd_list_tr)) ? "no" : "yes", (list_empty(&bd->bd_le.le_list)) ? "no" : "yes"); if (gl->gl_ops == &gfs2_inode_glops) { struct gfs2_inode *ip = gl->gl_object; unsigned int x; if (!ip) return; fs_warn(sdp, "ip = %llu %llu\n", (unsigned long long)ip->i_num.no_formal_ino, (unsigned long long)ip->i_num.no_addr); for (x = 0; x < GFS2_MAX_META_HEIGHT; x++) fs_warn(sdp, "ip->i_cache[%u] = %s\n", x, (ip->i_cache[x]) ? "!NULL" : "NULL"); } }
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); }