--- linux-ext3-0.0.2a/fs/ext3/inode.c.~1~ Mon Oct 18 15:38:05 1999 +++ linux-ext3-0.0.2a/fs/ext3/inode.c Mon Oct 18 15:38:14 1999 @@ -60,7 +60,6 @@ handle->h_sync = 1; ext3_mark_inode_dirty(handle, inode); inode->i_size = 0; - /* @@@ Need to be able to do multi-part truncates here! */ if (inode->i_blocks) ext3_truncate (inode); ext3_free_inode (handle, inode); --- linux-ext3-0.0.2a/fs/ext3/truncate.c.~1~ Mon Oct 18 15:38:05 1999 +++ linux-ext3-0.0.2a/fs/ext3/truncate.c Mon Oct 18 15:38:14 1999 @@ -130,22 +130,20 @@ EXT3_DATA_TRANS_BLOCKS + needed); } -static int extend_transaction(handle_t **handle, struct inode *inode) +static int extend_transaction(handle_t *handle, struct inode *inode) { long needed; - if ((*handle)->h_buffer_credits > EXT3_RESERVE_TRANS_BLOCKS) + if (handle->h_buffer_credits > EXT3_RESERVE_TRANS_BLOCKS) return 0; needed = inode->i_blocks; if (needed > EXT3_MAX_TRANS_DATA) needed = EXT3_MAX_TRANS_DATA; - if (journal_extend(*handle, EXT3_RESERVE_TRANS_BLOCKS + needed)) + if (!journal_extend(handle, EXT3_RESERVE_TRANS_BLOCKS + needed)) return 0; - journal_stop(*handle); - printk (KERN_DEBUG "extending handle %p\n", *handle); - *handle = start_transaction(inode); - printk (KERN_DEBUG "new handle %p\n", *handle); + jfs_debug(2, "extending handle %p\n", handle); + journal_restart(handle, EXT3_DATA_TRANS_BLOCKS + needed); return 1; } @@ -202,7 +200,7 @@ ext3_mark_inode_dirty(handle, inode); goto out; } - printk (KERN_DEBUG "retry 1\n"); + jfs_debug(4, "retry\n"); retry = 1; in_use: @@ -230,7 +228,7 @@ if (!tmp) continue; - extend_transaction(&handle, inode); + extend_transaction(handle, inode); bh = find_buffer(inode->i_dev, tmp, inode->i_sb->s_blocksize); if (bh) { @@ -319,7 +317,7 @@ if (!tmp) continue; - if (extend_transaction(&handle, inode)) + if (extend_transaction(handle, inode)) journal_get_write_access(handle, ind_bh); /* * Use find_buffer so we don't block here. --- linux-ext3-0.0.2a/fs/jfs/commit.c.~1~ Mon Oct 18 15:38:05 1999 +++ linux-ext3-0.0.2a/fs/jfs/commit.c Mon Oct 18 15:38:14 1999 @@ -67,11 +67,33 @@ sleep_on(&commit_transaction->t_wait); lock_journal(journal); } + + /* + * First thing we are allowed to do is to discard any remaining + * BJ_Reserved buffers. Note, it is _not_ permissible to assume + * that there are no such buffers: if a large filesystem + * operation like a truncate needs to split itself over multiple + * transactions, then it may try to do a journal_restart() while + * there are still BJ_Reserved buffers outstanding. These must + * be released cleanly from the current transaction. + * + * In this case, the filesystem must still reserve write access + * again before modifying the buffer in the new transaction, but + * we do not require it to remember exactly which old buffers it + * has reserved. This is consistent with the existing behaviour + * that multiple journal_get_write_access() calls to the same + * buffer are perfectly permissable. + */ + + while (commit_transaction->t_reserved_list) { + bh = commit_transaction->t_reserved_list; + journal_refile_buffer(bh); + } + /* * Start committing the old transaction: time to get a new - * running transaction for incoming filesystem updates - */ + * running transaction for incoming filesystem updates */ jfs_debug (3, "commit phase 1\n"); @@ -375,25 +397,7 @@ } journal_insert_checkpoint(bh, commit_transaction); - journal_unfile_buffer(bh); - - /* If the buffer is now unused, just drop it. If it has - been modified by a later transaction, add it to the - new transaction's metadata list. */ - - bh->b_transaction = bh->b_next_transaction; - bh->b_next_transaction = NULL; - - if (bh->b_transaction != NULL) { - journal_file_buffer(bh, - bh->b_transaction, - BJ_Metadata); - } - - /* If necessary, remove it from the global journaled - buffer list and replace it back on the main dirty - buffer list. */ - refile_buffer(bh); + journal_refile_buffer(bh); } /* Done with this transaction! */ --- linux-ext3-0.0.2a/fs/jfs/recovery.c.~1~ Mon Oct 18 15:38:05 1999 +++ linux-ext3-0.0.2a/fs/jfs/recovery.c Mon Oct 18 15:38:14 1999 @@ -248,7 +248,7 @@ * looking either the next descriptor block or * the final commit record. */ - jfs_debug(3, "JFS: checking block %d\n", + jfs_debug(3, "JFS: checking block %ld\n", next_log_block); err = jread(&bh, journal, next_log_block); if (err) @@ -272,7 +272,7 @@ if (tmp->h_magic == htonl(JFS_MAGIC_NUMBER)) { int blocktype = ntohl(tmp->h_blocktype); - jfs_debug(3, "Found magic %ld\n", blocktype); + jfs_debug(3, "Found magic %d\n", blocktype); if (blocktype == JFS_DESCRIPTOR_BLOCK) { /* Work out where the next descriptor @@ -287,7 +287,7 @@ brelse(bh); if (sequence == htonl(next_commit_ID)) goto commit; - jfs_debug(2, "found sequence %d, expected %d.\n", ntohl(sequence), next_commit_ID); + jfs_debug(2, "found sequence %ld, expected %d.\n", ntohl(sequence), next_commit_ID); goto finished; } } @@ -349,7 +349,7 @@ success = -EIO; printk (KERN_ERR "JFS: IO error recovering " - "block %d in log\n", + "block %ld in log\n", next_log_block-1); } else { J_ASSERT(obh != NULL); --- linux-ext3-0.0.2a/fs/jfs/transaction.c.~1~ Mon Oct 18 15:38:05 1999 +++ linux-ext3-0.0.2a/fs/jfs/transaction.c Mon Oct 18 15:38:14 1999 @@ -108,39 +108,19 @@ */ /* - * Obtain a new handle. - * - * We make sure that the transaction can guarantee at least nblocks of - * modified buffers in the log. We block until the log can guarantee - * that much space. - * - * This function is visible to journal users (like ext2fs), so is not - * called with the journal already locked. - * - * Return a pointer to a newly allocated handle, or NULL on failure + * start_this_handle: Given a handle, deal with any locking or stalling + * needed to make sure that there is enough journal space for the handle + * to begin. Attach the handle to a transaction and set up the + * transaction's buffer credits. */ -handle_t *journal_start (journal_t *journal, int nblocks) +static int start_this_handle(journal_t *journal, handle_t *handle) { - transaction_t * transaction; - handle_t *handle; + transaction_t *transaction; int needed; - - if (!journal) - return NULL; - - if (current->j_handle) { - current->j_handle->h_ref++; - return current->j_handle; - } - - handle = kmalloc (sizeof (handle_t), GFP_KERNEL); - if (!handle) - return NULL; + int nblocks = handle->h_buffer_credits; jfs_debug(4, "New handle %p going live.\n", handle); - memset (handle, 0, sizeof (handle_t)); - current->j_handle = handle; repeat: @@ -219,8 +199,6 @@ * use and add the handle to the running transaction. */ handle->h_transaction = transaction; - handle->h_buffer_credits = nblocks; - handle->h_ref = 1; transaction->t_outstanding_credits += nblocks; transaction->t_updates++; jfs_debug(4, "Handle %p given %d credits (total %d, free %d)\n", @@ -229,6 +207,45 @@ unlock_journal(journal); + return 0; +} + + +/* + * Obtain a new handle. + * + * We make sure that the transaction can guarantee at least nblocks of + * modified buffers in the log. We block until the log can guarantee + * that much space. + * + * This function is visible to journal users (like ext2fs), so is not + * called with the journal already locked. + * + * Return a pointer to a newly allocated handle, or NULL on failure + */ + +handle_t *journal_start (journal_t *journal, int nblocks) +{ + handle_t *handle; + + if (!journal) + return NULL; + + if (current->j_handle) { + current->j_handle->h_ref++; + return current->j_handle; + } + + handle = kmalloc (sizeof (handle_t), GFP_KERNEL); + if (!handle) + return NULL; + memset (handle, 0, sizeof (handle_t)); + + handle->h_buffer_credits = nblocks; + handle->h_ref = 1; + current->j_handle = handle; + + start_this_handle(journal, handle); /* @@@ Error? */ return handle; } @@ -258,16 +275,24 @@ lock_journal (journal); /* Don't extend a locked-down transaction! */ - if (handle->h_transaction->t_state != T_RUNNING) + if (handle->h_transaction->t_state != T_RUNNING) { + jfs_debug(3, "denied handle %p %d blocks: " + "transaction not running\n", handle, nblocks); goto error_out; + } if (log_space_left(journal) < - transaction->t_outstanding_credits + nblocks) + transaction->t_outstanding_credits + nblocks) { + jfs_debug(3, "denied handle %p %d blocks: " + "insufficient log space\n", handle, nblocks); goto error_out; + } handle->h_buffer_credits += nblocks; transaction->t_outstanding_credits += nblocks; result = 0; + + jfs_debug(3, "extended handle %p by %d\n", handle, nblocks); error_out: unlock_journal (journal); @@ -276,7 +301,42 @@ /* - * journal_get_write_access: notify intent to modify a buffer for metata + * journal_restart: restart a handle for a multi-transaction filesystem + * operation. + * + * If the journal_extend() call above fails to grant new buffer credits + * to a running handle, a call to journal_restart will commit the + * handle's transaction so far and reattach the handle to a new + * transaction capabable of guaranteeing the requested number of + * credits. + */ + +int journal_restart(handle_t *handle, int nblocks) +{ + transaction_t *transaction = handle->h_transaction; + journal_t *journal = transaction->t_journal; + + /* First unlink the handle from its current transaction, and + * start the commit on that. */ + + J_ASSERT (transaction->t_updates > 0); + J_ASSERT (current->j_handle == handle); + + transaction->t_outstanding_credits -= handle->h_buffer_credits; + transaction->t_updates--; + + if (!transaction->t_updates) + wake_up(&transaction->t_wait); + + log_start_commit(journal, transaction); + + handle->h_buffer_credits = nblocks; + return start_this_handle(journal, handle); +} + + +/* + * journal_get_write_access: notify intent to modify a buffer for metadata * (not data) update. * * If the buffer is already part of the current transaction, then there @@ -551,6 +611,8 @@ lock_journal(journal); mark_buffer_dirty(bh, 0); + + J_ASSERT(bh->b_transaction != NULL); /* * Metadata already on the current transaction list doesn't @@ -559,7 +621,8 @@ * leave it alone for now. */ - if (bh->b_transaction && bh->b_transaction != transaction) { + if (bh->b_transaction != transaction) { + J_ASSERT (bh->b_transaction == journal->j_committing_transaction); J_ASSERT (bh->b_next_transaction == transaction); /* And this case is illegal: we can't reuse another * transaction's data buffer, ever. */ @@ -590,7 +653,20 @@ void journal_release_buffer (handle_t *handle, struct buffer_head *bh) { - jfs_ENOSYS(); + transaction_t *transaction = handle->h_transaction; + journal_t *journal = transaction->t_journal; + lock_journal(journal); + + /* If the buffer is reserved but not modified by this + * transaction, then it is safe to release it. In all other + * cases, just leave the buffer as it is. */ + + if (bh->b_jlist == BJ_Reserved && bh->b_transaction == transaction) { + handle->h_buffer_credits++; + journal_refile_buffer(bh); + } + + unlock_journal(journal); } @@ -805,9 +881,6 @@ J_ASSERT (transaction != 0); switch (buf->b_jlist) { - case BJ_Reserved: - buf->b_jlist = BJ_None; - /* Drop through: */ case BJ_None: return; case BJ_Data: @@ -830,6 +903,9 @@ case BJ_LogCtl: list = &transaction->t_log_list; break; + case BJ_Reserved: + list = &transaction->t_reserved_list; + break; } blist_del_buffer(list, buf); @@ -880,12 +956,43 @@ list = &transaction->t_log_list; break; case BJ_Reserved: - buf->b_jlist = jlist; - return; + list = &transaction->t_reserved_list; + break; } blist_add_buffer(list, buf); buf->b_jlist = jlist; } + +/* + * Remove a buffer from its current buffer list in preparation for + * dropping it from its current transaction entirely. If the buffer has + * already started to be used by a subsequent transaction, refile the + * buffer on that transaction's metadata list. + */ + +void journal_refile_buffer(struct buffer_head *bh) +{ + journal_unfile_buffer(bh); + + /* If the buffer is now unused, just drop it. If it has been + modified by a later transaction, add it to the new + transaction's metadata list. */ + + bh->b_transaction = bh->b_next_transaction; + bh->b_next_transaction = NULL; + + if (bh->b_transaction != NULL) { + int tstate; + journal_file_buffer(bh, bh->b_transaction, BJ_Metadata); + tstate = bh->b_transaction->t_state; + J_ASSERT(tstate == T_RUNNING); + } + + /* If necessary, remove it from the global journaled + buffer list and replace it back on the main dirty + buffer list. */ + refile_buffer(bh); +} --- linux-ext3-0.0.2a/include/linux/jfs.h.~1~ Mon Oct 18 15:38:05 1999 +++ linux-ext3-0.0.2a/include/linux/jfs.h Mon Oct 18 16:14:57 1999 @@ -194,6 +194,10 @@ /* Number of buffers on the t_buffers list */ int t_nr_buffers; + /* Doubly-linked circular list of all buffers reserved but not + yet modified by this transaction */ + struct buffer_head * t_reserved_list; + /* Doubly-linked circular list of all metadata buffers owned by this transaction */ struct buffer_head * t_buffers; @@ -378,6 +382,7 @@ /* Filing buffers */ extern void journal_unfile_buffer(struct buffer_head *); +extern void journal_refile_buffer(struct buffer_head *); extern void journal_file_buffer(struct buffer_head *, transaction_t *, int); extern void journal_clean_data_list(transaction_t *transaction); @@ -443,6 +448,7 @@ */ extern handle_t *journal_start (journal_t *, int nblocks); +extern int journal_restart (handle_t *, int nblocks); extern int journal_extend (handle_t *, int nblocks); extern int journal_get_write_access (handle_t *, struct buffer_head *); extern int journal_get_create_access (handle_t *, struct buffer_head *); --- linux-ext3-0.0.2a/kernel/ksyms.c.~1~ Mon Oct 18 18:11:08 1999 +++ linux-ext3-0.0.2a/kernel/ksyms.c Mon Oct 18 15:49:37 1999 @@ -69,6 +69,9 @@ }; #endif +extern void jfs_preclean_buffer_check(struct buffer_head *); +extern void jfs_prelock_buffer_check(struct buffer_head *); + #ifdef CONFIG_KMOD EXPORT_SYMBOL(request_module); @@ -229,6 +232,10 @@ EXPORT_SYMBOL(max_segments); EXPORT_SYMBOL(max_readahead); EXPORT_SYMBOL(show_buffers); + +/* JFS debugging only: */ +EXPORT_SYMBOL(jfs_prelock_buffer_check); +EXPORT_SYMBOL(jfs_preclean_buffer_check); /* tty routines */ EXPORT_SYMBOL(tty_hangup);