Index: linux-2.6.18-128.1.6/include/linux/jbd2.h =================================================================== --- linux-2.6.18-128.1.6.orig/include/linux/jbd2.h 2009-04-15 08:35:28.000000000 +0530 +++ linux-2.6.18-128.1.6/include/linux/jbd2.h 2009-05-28 15:10:18.000000000 +0530 @@ -381,6 +381,27 @@ bit_spin_unlock(BH_JournalHead, &bh->b_state); } +#define HAVE_JOURNAL_CALLBACK_STATUS +/** + * struct journal_callback - Base structure for callback information. + * @jcb_list: list information for other callbacks attached to the same handle. + * @jcb_func: Function to call with this callback structure. + * + * This struct is a 'seed' structure for a using with your own callback + * structs. If you are using callbacks you must allocate one of these + * or another struct of your own definition which has this struct + * as it's first element and pass it to journal_callback_set(). + * + * This is used internally by jbd2 to maintain callback information. + * + * See journal_callback_set for more information. + **/ +struct journal_callback { + struct list_head jcb_list; /* t_jcb_lock */ + void (*jcb_func)(struct journal_callback *jcb, int error); + /* user data goes here */ +}; + struct jbd2_revoke_table_s; /** @@ -389,6 +410,7 @@ * @h_transaction: Which compound transaction is this update a part of? * @h_buffer_credits: Number of remaining buffers we are allowed to dirty. * @h_ref: Reference count on this handle + * @h_jcb: List of application registered callbacks for this handle. * @h_err: Field for caller's use to track errors through large fs operations * @h_sync: flag for sync-on-close * @h_jdata: flag to force data journaling @@ -414,6 +436,13 @@ /* operations */ int h_err; + /* + * List of application registered callbacks for this handle. The + * function(s) will be called after the transaction that this handle is + * part of has been committed to disk. [t_jcb_lock] + */ + struct list_head h_jcb; + /* Flags [no locking] */ unsigned int h_sync: 1; /* sync-on-close */ unsigned int h_jdata: 1; /* force data journaling */ @@ -469,6 +498,8 @@ * j_state_lock * ->j_list_lock (journal_unmap_buffer) * + * t_handle_lock + * ->t_jcb_lock */ struct transaction_s @@ -615,6 +646,15 @@ */ int t_handle_count; + /* + * Protects the callback list + */ + spinlock_t t_jcb_lock; + /* + * List of registered callback functions for this transaction. + * Called when the transaction is committed. [t_jcb_lock] + */ + struct list_head t_jcb; /* * For use by the filesystem to store fs-specific data * structures associated with the transaction @@ -1018,6 +1058,9 @@ extern int jbd2_journal_flush (journal_t *); extern void jbd2_journal_lock_updates (journal_t *); extern void jbd2_journal_unlock_updates (journal_t *); +extern void jbd2_journal_callback_set(handle_t *handle, + void (*fn)(struct journal_callback *,int), + struct journal_callback *jcb); extern journal_t * jbd2_journal_init_dev(struct block_device *bdev, struct block_device *fs_dev, Index: linux-2.6.18-128.1.6/fs/jbd2/checkpoint.c =================================================================== --- linux-2.6.18-128.1.6.orig/fs/jbd2/checkpoint.c 2009-04-15 08:35:28.000000000 +0530 +++ linux-2.6.18-128.1.6/fs/jbd2/checkpoint.c 2009-05-28 15:10:18.000000000 +0530 @@ -695,6 +695,7 @@ J_ASSERT(transaction->t_checkpoint_list == NULL); J_ASSERT(transaction->t_checkpoint_io_list == NULL); J_ASSERT(transaction->t_updates == 0); + J_ASSERT(list_empty(&transaction->t_jcb)); J_ASSERT(journal->j_committing_transaction != transaction); J_ASSERT(journal->j_running_transaction != transaction); Index: linux-2.6.18-128.1.6/fs/jbd2/commit.c =================================================================== --- linux-2.6.18-164.6.1/fs/jbd2/commit.c 2010-01-21 11:24:52.000000000 +0530 +++ linux-2.6.18-164.6.1_new/fs/jbd2/commit.c 2010-01-21 11:26:36.000000000 +0530 @@ -832,6 +832,29 @@ wait_for_iobuf: processing: any buffers committed as a result of this transaction can be removed from any checkpoint list it was on before. */ + /* + * Call any callbacks that had been registered for handles in this + * transaction. It is up to the callback to free any allocated + * memory. + * + * The spinlocking (t_jcb_lock) here is surely unnecessary... + */ + spin_lock(&commit_transaction->t_jcb_lock); + if (!list_empty(&commit_transaction->t_jcb)) { + struct list_head *p, *n; + int error = is_journal_aborted(journal); + + list_for_each_safe(p, n, &commit_transaction->t_jcb) { + struct journal_callback *jcb; + + jcb = list_entry(p, struct journal_callback, jcb_list); + list_del(p); + spin_unlock(&commit_transaction->t_jcb_lock); + jcb->jcb_func(jcb, error); + spin_lock(&commit_transaction->t_jcb_lock); + } + } + spin_unlock(&commit_transaction->t_jcb_lock); jbd_debug(3, "JBD: commit phase 6\n"); Index: linux-2.6.18-128.1.6/fs/jbd2/journal.c =================================================================== --- linux-2.6.18-128.1.6.orig/fs/jbd2/journal.c 2009-04-15 08:35:28.000000000 +0530 +++ linux-2.6.18-128.1.6/fs/jbd2/journal.c 2009-05-28 17:13:35.000000000 +0530 @@ -80,6 +80,9 @@ EXPORT_SYMBOL(jbd2_journal_init_jbd_inode); EXPORT_SYMBOL(jbd2_journal_release_jbd_inode); EXPORT_SYMBOL(jbd2_journal_begin_ordered_truncate); +EXPORT_SYMBOL(jbd2_journal_callback_set); +EXPORT_SYMBOL(jbd2_journal_bmap); +EXPORT_SYMBOL(jbd2_log_start_commit); static int journal_convert_superblock_v1(journal_t *, journal_superblock_t *); static void __journal_abort_soft (journal_t *journal, int errno); Index: linux-2.6.18-128.1.6/fs/jbd2/transaction.c =================================================================== --- linux-2.6.18-128.1.6.orig/fs/jbd2/transaction.c 2009-04-15 08:35:28.000000000 +0530 +++ linux-2.6.18-128.1.6/fs/jbd2/transaction.c 2009-05-28 15:11:28.000000000 +0530 @@ -51,6 +51,9 @@ spin_lock_init(&transaction->t_handle_lock); INIT_LIST_HEAD(&transaction->t_inode_list); INIT_LIST_HEAD(&transaction->t_private_list); + INIT_LIST_HEAD(&transaction->t_jcb); + spin_lock_init(&transaction->t_jcb_lock); + /* Set up the commit timer for the new transaction. */ journal->j_commit_timer.expires = round_jiffies(transaction->t_expires); @@ -251,6 +254,7 @@ memset(handle, 0, sizeof(*handle)); handle->h_buffer_credits = nblocks; handle->h_ref = 1; + INIT_LIST_HEAD(&handle->h_jcb); lockdep_init_map(&handle->h_lockdep_map, "jbd2_handle", &jbd2_handle_key, 0); @@ -1349,6 +1353,36 @@ } /** + * void jbd2_journal_callback_set() - Register a callback function for this handle. + * @handle: handle to attach the callback to. + * @func: function to callback. + * @jcb: structure with additional information required by func() , and + * some space for jbd2 internal information. + * + * The function will be + * called when the transaction that this handle is part of has been + * committed to disk with the original callback data struct and the + * error status of the journal as parameters. There is no guarantee of + * ordering between handles within a single transaction, nor between + * callbacks registered on the same handle. + * + * The caller is responsible for allocating the journal_callback struct. + * This is to allow the caller to add as much extra data to the callback + * as needed, but reduce the overhead of multiple allocations. The caller + * allocated struct must start with a struct journal_callback at offset 0, + * and has the caller-specific data afterwards. + */ +void jbd2_journal_callback_set(handle_t *handle, + void (*func)(struct journal_callback *jcb, int error), + struct journal_callback *jcb) +{ + spin_lock(&handle->h_transaction->t_jcb_lock); + list_add_tail(&jcb->jcb_list, &handle->h_jcb); + spin_unlock(&handle->h_transaction->t_jcb_lock); + jcb->jcb_func = func; +} + +/** * int jbd2_journal_stop() - complete a transaction * @handle: tranaction to complete. * @@ -1422,6 +1456,11 @@ wake_up(&journal->j_wait_transaction_locked); } + /* Move callbacks from the handle to the transaction. */ + spin_lock(&transaction->t_jcb_lock); + list_splice(&handle->h_jcb, &transaction->t_jcb); + spin_unlock(&transaction->t_jcb_lock); + /* * If the handle is marked SYNC, we need to set another commit * going! We also want to force a commit if the current