]> git.hungrycats.org Git - linux/commitdiff
[PATCH] JBD commit callback capability
authorAndrew Morton <akpm@zip.com.au>
Thu, 4 Jul 2002 15:31:50 +0000 (08:31 -0700)
committerLinus Torvalds <torvalds@home.transmeta.com>
Thu, 4 Jul 2002 15:31:50 +0000 (08:31 -0700)
This is a patch which Stephen has applied to ext3's 2.4 repository.
Originally written by Andreas, generalised somewhat by Stephen.

Add jbd callback mechanism, requested for InterMezzo.  We allow the jbd's
client to request notification when a given handle's IO finally commits to
disk, so that clients can manage their own writeback state asynchronously.

fs/jbd/checkpoint.c
fs/jbd/commit.c
fs/jbd/journal.c
fs/jbd/transaction.c
include/linux/jbd.h

index 428dcd822dec0bd62623015603744f1ee50cb865..d640e23a1bf1ca003dc0f38ad5379bf0950a84db 100644 (file)
@@ -592,7 +592,8 @@ void __journal_drop_transaction(journal_t *journal, transaction_t *transaction)
        J_ASSERT (transaction->t_log_list == NULL);
        J_ASSERT (transaction->t_checkpoint_list == NULL);
        J_ASSERT (transaction->t_updates == 0);
-       
+       J_ASSERT (list_empty(&transaction->t_jcb));
+
        J_ASSERT (transaction->t_journal->j_committing_transaction !=
                                        transaction);
        
index 2283894a81a64adf2c9e4c074d556c9ba1a27bdf..abbd16f1e8229a2b2c8785fcb1060cb783af780f 100644 (file)
@@ -471,7 +471,7 @@ start_journal_io:
            transaction's t_log_list queue, and metadata buffers are on
            the t_iobuf_list queue.
 
-          Wait for the transactions in reverse order.  That way we are
+          Wait for the buffers in reverse order.  That way we are
           less likely to be woken up until all IOs have completed, and
           so we incur less scheduling load.
        */
@@ -563,8 +563,10 @@ start_journal_io:
 
        jbd_debug(3, "JBD: commit phase 6\n");
 
-       if (is_journal_aborted(journal))
+       if (is_journal_aborted(journal)) {
+               unlock_journal(journal);
                goto skip_commit;
+       }
 
        /* Done it all: now write the commit record.  We should have
         * cleaned up our previous buffers by now, so if we are in abort
@@ -574,9 +576,10 @@ start_journal_io:
        descriptor = journal_get_descriptor_buffer(journal);
        if (!descriptor) {
                __journal_abort_hard(journal);
+               unlock_journal(journal);
                goto skip_commit;
        }
-       
+
        /* AKPM: buglet - add `i' to tmp! */
        for (i = 0; i < jh2bh(descriptor)->b_size; i += 512) {
                journal_header_t *tmp =
@@ -596,14 +599,32 @@ start_journal_io:
                __brelse(bh);           /* One for getblk() */
                journal_unlock_journal_head(descriptor);
        }
-       lock_journal(journal);
 
        /* End of a transaction!  Finally, we can do checkpoint
            processing: any buffers committed as a result of this
            transaction can be removed from any checkpoint list it was on
            before. */
 
-skip_commit:
+skip_commit: /* The journal should be unlocked by now. */
+
+       /* Call any callbacks that had been registered for handles in this
+        * transaction.  It is up to the callback to free any allocated
+        * memory.
+        */
+       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);
+                       jcb->jcb_func(jcb, error);
+               }
+       }
+
+       lock_journal(journal);
 
        jbd_debug(3, "JBD: commit phase 7\n");
 
index ade37ad43606d1360b41f1b0c9c402cf3916295b..3d5c7d921e6044e09ae37c93d9f5699e07000cf1 100644 (file)
@@ -58,6 +58,7 @@ EXPORT_SYMBOL(journal_sync_buffer);
 #endif
 EXPORT_SYMBOL(journal_flush);
 EXPORT_SYMBOL(journal_revoke);
+EXPORT_SYMBOL(journal_callback_set);
 
 EXPORT_SYMBOL(journal_init_dev);
 EXPORT_SYMBOL(journal_init_inode);
index 37c9ed30ebfd8b9cb3dfeb4741c585ead7bf7ed8..860b2685ebd682d0016993334321ff561d8a9dd5 100644 (file)
@@ -57,6 +57,7 @@ static transaction_t * get_transaction (journal_t * journal, int is_try)
        transaction->t_state = T_RUNNING;
        transaction->t_tid = journal->j_transaction_sequence++;
        transaction->t_expires = jiffies + journal->j_commit_interval;
+       INIT_LIST_HEAD(&transaction->t_jcb);
 
        /* Set up the commit timer for the new transaction. */
        J_ASSERT (!journal->j_commit_timer_active);
@@ -90,7 +91,14 @@ static int start_this_handle(journal_t *journal, handle_t *handle)
        transaction_t *transaction;
        int needed;
        int nblocks = handle->h_buffer_credits;
-       
+
+       if (nblocks > journal->j_max_transaction_buffers) {
+               printk(KERN_ERR "JBD: %s wants too many credits (%d > %d)\n",
+                      current->comm, nblocks,
+                      journal->j_max_transaction_buffers);
+               return -ENOSPC;
+       }
+
        jbd_debug(3, "New handle %p going live.\n", handle);
 
 repeat:
@@ -200,6 +208,20 @@ repeat_locked:
        return 0;
 }
 
+/* Allocate a new handle.  This should probably be in a slab... */
+static handle_t *new_handle(int nblocks)
+{
+       handle_t *handle = jbd_kmalloc(sizeof (handle_t), GFP_NOFS);
+       if (!handle)
+               return NULL;
+       memset(handle, 0, sizeof (handle_t));
+       handle->h_buffer_credits = nblocks;
+       handle->h_ref = 1;
+       INIT_LIST_HEAD(&handle->h_jcb);
+
+       return handle;
+}
+
 /*
  * Obtain a new handle.  
  *
@@ -226,14 +248,11 @@ handle_t *journal_start(journal_t *journal, int nblocks)
                handle->h_ref++;
                return handle;
        }
-       
-       handle = jbd_kmalloc(sizeof (handle_t), GFP_NOFS);
+
+       handle = new_handle(nblocks);
        if (!handle)
                return ERR_PTR(-ENOMEM);
-       memset (handle, 0, sizeof (handle_t));
 
-       handle->h_buffer_credits = nblocks;
-       handle->h_ref = 1;
        current->journal_info = handle;
 
        err = start_this_handle(journal, handle);
@@ -332,14 +351,11 @@ handle_t *journal_try_start(journal_t *journal, int nblocks)
        
        if (is_journal_aborted(journal))
                return ERR_PTR(-EIO);
-       
-       handle = jbd_kmalloc(sizeof (handle_t), GFP_NOFS);
+
+       handle = new_handle(nblocks);
        if (!handle)
                return ERR_PTR(-ENOMEM);
-       memset (handle, 0, sizeof (handle_t));
 
-       handle->h_buffer_credits = nblocks;
-       handle->h_ref = 1;
        current->journal_info = handle;
 
        err = try_start_this_handle(journal, handle);
@@ -1347,6 +1363,28 @@ out:
 }
 #endif
 
+/*
+ * Register a callback function for this handle.  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 journal_callback_set(handle_t *handle,
+                         void (*func)(struct journal_callback *jcb, int error),
+                         struct journal_callback *jcb)
+{
+       list_add_tail(&jcb->jcb_list, &handle->h_jcb);
+       jcb->jcb_func = func;
+}
+
 /*
  * All done for a particular handle.
  *
@@ -1411,7 +1449,10 @@ int journal_stop(handle_t *handle)
                        wake_up(&journal->j_wait_transaction_locked);
        }
 
-       /* 
+       /* Move callbacks from the handle to the transaction. */
+       list_splice(&handle->h_jcb, &transaction->t_jcb);
+
+       /*
         * If the handle is marked SYNC, we need to set another commit
         * going!  We also want to force a commit if the current
         * transaction is occupying too much of the log, or if the
index 683c1247fd709eea4e7e7943c6bb0b63f108ecc8..fafb868eec1151f6ad7b1bdf4869b5d81291b3bd 100644 (file)
@@ -250,6 +250,13 @@ static inline struct journal_head *bh2jh(struct buffer_head *bh)
        return bh->b_private;
 }
 
+#define HAVE_JOURNAL_CALLBACK_STATUS
+struct journal_callback {
+       struct list_head jcb_list;
+       void (*jcb_func)(struct journal_callback *jcb, int error);
+       /* user data goes here */
+};
+
 struct jbd_revoke_table_s;
 
 /* The handle_t type represents a single atomic update being performed
@@ -280,6 +287,12 @@ struct handle_s
           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.
+        */
+       struct list_head        h_jcb;
+
        /* Flags */
        unsigned int    h_sync:         1;      /* sync-on-close */
        unsigned int    h_jdata:        1;      /* force data journaling */
@@ -399,6 +412,10 @@ struct transaction_s
 
        /* How many handles used this transaction? */
        int t_handle_count;
+
+       /* List of registered callback functions for this transaction.
+        * Called when the transaction is committed. */
+       struct list_head        t_jcb;
 };
 
 
@@ -647,6 +664,9 @@ extern int   journal_invalidatepage(journal_t *,
 extern int      journal_try_to_free_buffers(journal_t *, struct page *, int);
 extern int      journal_stop(handle_t *);
 extern int      journal_flush (journal_t *);
+extern void     journal_callback_set(handle_t *handle,
+                                     void (*fn)(struct journal_callback *,int),
+                                     struct journal_callback *jcb);
 
 extern void     journal_lock_updates (journal_t *);
 extern void     journal_unlock_updates (journal_t *);