#include <linux/gfs2_ondisk.h>
#include <linux/crc32.h>
#include <linux/lm_interface.h>
+#include <linux/delay.h>
#include "gfs2.h"
#include "incore.h"
gfs2_assert(sdp, bd->bd_ail == ai);
+ if (!bh){
+ list_move(&bd->bd_ail_st_list, &ai->ai_ail2_list);
+ continue;
+ }
+
if (!buffer_busy(bh)) {
if (!buffer_uptodate(bh)) {
gfs2_log_unlock(sdp);
bd_ail_st_list) {
bh = bd->bd_bh;
+ if (!bh){
+ list_move(&bd->bd_ail_st_list, &ai->ai_ail2_list);
+ continue;
+ }
+
gfs2_assert(sdp, bd->bd_ail == ai);
if (buffer_busy(bh)) {
return list_empty(&ai->ai_ail1_list);
}
-void gfs2_ail1_start(struct gfs2_sbd *sdp, int flags)
+static void gfs2_ail1_start(struct gfs2_sbd *sdp, int flags)
{
struct list_head *head = &sdp->sd_ail1_list;
u64 sync_gen;
* @sdp: The GFS2 superblock
* @blks: The number of blocks to reserve
*
+ * Note that we never give out the last few blocks of the journal. Thats
+ * due to the fact that there is a small number of header blocks
+ * associated with each log flush. The exact number can't be known until
+ * flush time, so we ensure that we have just enough free blocks at all
+ * times to avoid running out during a log flush.
+ *
* Returns: errno
*/
int gfs2_log_reserve(struct gfs2_sbd *sdp, unsigned int blks)
{
unsigned int try = 0;
+ unsigned reserved_blks = 6 * (4096 / sdp->sd_vfs->s_blocksize);
if (gfs2_assert_warn(sdp, blks) ||
gfs2_assert_warn(sdp, blks <= sdp->sd_jdesc->jd_blocks))
mutex_lock(&sdp->sd_log_reserve_mutex);
gfs2_log_lock(sdp);
- while(sdp->sd_log_blks_free <= blks) {
+ while(sdp->sd_log_blks_free <= (blks + reserved_blks)) {
gfs2_log_unlock(sdp);
gfs2_ail1_empty(sdp, 0);
gfs2_log_flush(sdp, NULL);
static u64 log_bmap(struct gfs2_sbd *sdp, unsigned int lbn)
{
+ struct inode *inode = sdp->sd_jdesc->jd_inode;
int error;
- struct buffer_head bh_map;
+ struct buffer_head bh_map = { .b_state = 0, .b_blocknr = 0 };
- error = gfs2_block_map(sdp->sd_jdesc->jd_inode, lbn, 0, &bh_map, 1);
+ bh_map.b_size = 1 << inode->i_blkbits;
+ error = gfs2_block_map(inode, lbn, 0, &bh_map);
if (error || !bh_map.b_blocknr)
- printk(KERN_INFO "error=%d, dbn=%llu lbn=%u", error, bh_map.b_blocknr, lbn);
+ printk(KERN_INFO "error=%d, dbn=%llu lbn=%u", error,
+ (unsigned long long)bh_map.b_blocknr, lbn);
gfs2_assert_withdraw(sdp, !error && bh_map.b_blocknr);
return bh_map.b_blocknr;
return dist;
}
+/**
+ * calc_reserved - Calculate the number of blocks to reserve when
+ * refunding a transaction's unused buffers.
+ * @sdp: The GFS2 superblock
+ *
+ * This is complex. We need to reserve room for all our currently used
+ * metadata buffers (e.g. normal file I/O rewriting file time stamps) and
+ * all our journaled data buffers for journaled files (e.g. files in the
+ * meta_fs like rindex, or files for which chattr +j was done.)
+ * If we don't reserve enough space, gfs2_log_refund and gfs2_log_flush
+ * will count it as free space (sd_log_blks_free) and corruption will follow.
+ *
+ * We can have metadata bufs and jdata bufs in the same journal. So each
+ * type gets its own log header, for which we need to reserve a block.
+ * In fact, each type has the potential for needing more than one header
+ * in cases where we have more buffers than will fit on a journal page.
+ * Metadata journal entries take up half the space of journaled buffer entries.
+ * Thus, metadata entries have buf_limit (502) and journaled buffers have
+ * databuf_limit (251) before they cause a wrap around.
+ *
+ * Also, we need to reserve blocks for revoke journal entries and one for an
+ * overall header for the lot.
+ *
+ * Returns: the number of blocks reserved
+ */
+static unsigned int calc_reserved(struct gfs2_sbd *sdp)
+{
+ unsigned int reserved = 0;
+ unsigned int mbuf_limit, metabufhdrs_needed;
+ unsigned int dbuf_limit, databufhdrs_needed;
+ unsigned int revokes = 0;
+
+ mbuf_limit = buf_limit(sdp);
+ metabufhdrs_needed = (sdp->sd_log_commited_buf +
+ (mbuf_limit - 1)) / mbuf_limit;
+ dbuf_limit = databuf_limit(sdp);
+ databufhdrs_needed = (sdp->sd_log_commited_databuf +
+ (dbuf_limit - 1)) / dbuf_limit;
+
+ if (sdp->sd_log_commited_revoke)
+ revokes = gfs2_struct2blk(sdp, sdp->sd_log_commited_revoke,
+ sizeof(u64));
+
+ reserved = sdp->sd_log_commited_buf + metabufhdrs_needed +
+ sdp->sd_log_commited_databuf + databufhdrs_needed +
+ revokes;
+ /* One for the overall header */
+ if (reserved)
+ reserved++;
+ return reserved;
+}
+
static unsigned int current_tail(struct gfs2_sbd *sdp)
{
struct gfs2_ail *ai;
return bh;
}
-static void log_pull_tail(struct gfs2_sbd *sdp, unsigned int new_tail, int pull)
+static void log_pull_tail(struct gfs2_sbd *sdp, unsigned int new_tail)
{
unsigned int dist = log_distance(sdp, new_tail, sdp->sd_log_tail);
ail2_empty(sdp, new_tail);
gfs2_log_lock(sdp);
- sdp->sd_log_blks_free += dist - (pull ? 1 : 0);
+ sdp->sd_log_blks_free += dist;
gfs2_assert_withdraw(sdp, sdp->sd_log_blks_free <= sdp->sd_jdesc->jd_blocks);
gfs2_log_unlock(sdp);
brelse(bh);
if (sdp->sd_log_tail != tail)
- log_pull_tail(sdp, tail, pull);
+ log_pull_tail(sdp, tail);
else
gfs2_assert_withdraw(sdp, !pull);
struct list_head *head = &sdp->sd_log_flush_list;
struct gfs2_log_buf *lb;
struct buffer_head *bh;
+ int flushcount = 0;
while (!list_empty(head)) {
lb = list_entry(head->next, struct gfs2_log_buf, lb_list);
} else
brelse(bh);
kfree(lb);
+ flushcount++;
}
- log_write_header(sdp, 0, 0);
+ /* If nothing was journaled, the header is unplanned and unwanted. */
+ if (flushcount) {
+ log_write_header(sdp, 0, 0);
+ } else {
+ unsigned int tail;
+ tail = current_tail(sdp);
+
+ gfs2_ail1_empty(sdp, 0);
+ if (sdp->sd_log_tail != tail)
+ log_pull_tail(sdp, tail);
+ }
}
/**
INIT_LIST_HEAD(&ai->ai_ail1_list);
INIT_LIST_HEAD(&ai->ai_ail2_list);
- gfs2_assert_withdraw(sdp, sdp->sd_log_num_buf == sdp->sd_log_commited_buf);
+ gfs2_assert_withdraw(sdp,
+ sdp->sd_log_num_buf + sdp->sd_log_num_jdata ==
+ sdp->sd_log_commited_buf +
+ sdp->sd_log_commited_databuf);
gfs2_assert_withdraw(sdp,
sdp->sd_log_num_revoke == sdp->sd_log_commited_revoke);
lops_before_commit(sdp);
if (!list_empty(&sdp->sd_log_flush_list))
log_flush_commit(sdp);
- else if (sdp->sd_log_tail != current_tail(sdp) && !sdp->sd_log_idle)
+ else if (sdp->sd_log_tail != current_tail(sdp) && !sdp->sd_log_idle){
+ gfs2_log_lock(sdp);
+ sdp->sd_log_blks_free--; /* Adjust for unreserved buffer */
+ gfs2_log_unlock(sdp);
log_write_header(sdp, 0, PULL);
+ }
lops_after_commit(sdp, ai);
gfs2_log_lock(sdp);
sdp->sd_log_head = sdp->sd_log_flush_head;
- sdp->sd_log_blks_free -= sdp->sd_log_num_hdrs;
sdp->sd_log_blks_reserved = 0;
sdp->sd_log_commited_buf = 0;
- sdp->sd_log_num_hdrs = 0;
+ sdp->sd_log_commited_databuf = 0;
sdp->sd_log_commited_revoke = 0;
if (!list_empty(&ai->ai_ail1_list)) {
static void log_refund(struct gfs2_sbd *sdp, struct gfs2_trans *tr)
{
- unsigned int reserved = 0;
+ unsigned int reserved;
unsigned int old;
gfs2_log_lock(sdp);
sdp->sd_log_commited_buf += tr->tr_num_buf_new - tr->tr_num_buf_rm;
- gfs2_assert_withdraw(sdp, ((int)sdp->sd_log_commited_buf) >= 0);
+ sdp->sd_log_commited_databuf += tr->tr_num_databuf_new -
+ tr->tr_num_databuf_rm;
+ gfs2_assert_withdraw(sdp, (((int)sdp->sd_log_commited_buf) >= 0) ||
+ (((int)sdp->sd_log_commited_databuf) >= 0));
sdp->sd_log_commited_revoke += tr->tr_num_revoke - tr->tr_num_revoke_rm;
gfs2_assert_withdraw(sdp, ((int)sdp->sd_log_commited_revoke) >= 0);
-
- if (sdp->sd_log_commited_buf)
- reserved += sdp->sd_log_commited_buf;
- if (sdp->sd_log_commited_revoke)
- reserved += gfs2_struct2blk(sdp, sdp->sd_log_commited_revoke,
- sizeof(u64));
- if (reserved)
- reserved++;
-
+ reserved = calc_reserved(sdp);
old = sdp->sd_log_blks_free;
sdp->sd_log_blks_free += tr->tr_reserved -
(reserved - sdp->sd_log_blks_reserved);
gfs2_assert_withdraw(sdp, sdp->sd_log_blks_free >= old);
- gfs2_assert_withdraw(sdp,
- sdp->sd_log_blks_free <= sdp->sd_jdesc->jd_blocks +
- sdp->sd_log_num_hdrs);
+ gfs2_assert_withdraw(sdp, sdp->sd_log_blks_free <=
+ sdp->sd_jdesc->jd_blocks);
sdp->sd_log_blks_reserved = reserved;
up_read(&sdp->sd_log_flush_lock);
gfs2_log_lock(sdp);
- if (sdp->sd_log_num_buf > gfs2_tune_get(sdp, gt_incore_log_blocks)) {
- gfs2_log_unlock(sdp);
- gfs2_log_flush(sdp, NULL);
- } else {
- gfs2_log_unlock(sdp);
- }
+ if (sdp->sd_log_num_buf > gfs2_tune_get(sdp, gt_incore_log_blocks))
+ wake_up_process(sdp->sd_logd_process);
+ gfs2_log_unlock(sdp);
}
/**
gfs2_assert_withdraw(sdp, !sdp->sd_log_num_revoke);
gfs2_assert_withdraw(sdp, !sdp->sd_log_num_rg);
gfs2_assert_withdraw(sdp, !sdp->sd_log_num_databuf);
- gfs2_assert_withdraw(sdp, !sdp->sd_log_num_hdrs);
gfs2_assert_withdraw(sdp, list_empty(&sdp->sd_ail1_list));
sdp->sd_log_flush_head = sdp->sd_log_head;
sdp->sd_log_flush_wrapped = 0;
- log_write_header(sdp, GFS2_LOG_HEAD_UNMOUNT, 0);
+ log_write_header(sdp, GFS2_LOG_HEAD_UNMOUNT,
+ (sdp->sd_log_tail == current_tail(sdp)) ? 0 : PULL);
gfs2_assert_warn(sdp, sdp->sd_log_blks_free == sdp->sd_jdesc->jd_blocks);
gfs2_assert_warn(sdp, sdp->sd_log_head == sdp->sd_log_tail);
up_write(&sdp->sd_log_flush_lock);
}
+
+/**
+ * gfs2_meta_syncfs - sync all the buffers in a filesystem
+ * @sdp: the filesystem
+ *
+ */
+
+void gfs2_meta_syncfs(struct gfs2_sbd *sdp)
+{
+ gfs2_log_flush(sdp, NULL);
+ for (;;) {
+ gfs2_ail1_start(sdp, DIO_ALL);
+ if (gfs2_ail1_empty(sdp, DIO_ALL))
+ break;
+ msleep(10);
+ }
+}
+