]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - fs/jbd2/transaction.c
Merge branch 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4
[linux-2.6-omap-h63xx.git] / fs / jbd2 / transaction.c
index 39b7805a599a79fd80c0150073cbf0f473fe8bcf..46b4e347ed7d9f3949df7b2b475bda7d7f2b8fef 100644 (file)
@@ -25,6 +25,7 @@
 #include <linux/timer.h>
 #include <linux/mm.h>
 #include <linux/highmem.h>
+#include <linux/hrtimer.h>
 
 static void __jbd2_journal_temp_unlink_buffer(struct journal_head *jh);
 
@@ -48,6 +49,7 @@ jbd2_get_transaction(journal_t *journal, transaction_t *transaction)
 {
        transaction->t_journal = journal;
        transaction->t_state = T_RUNNING;
+       transaction->t_start_time = ktime_get();
        transaction->t_tid = journal->j_transaction_sequence++;
        transaction->t_expires = jiffies + journal->j_commit_interval;
        spin_lock_init(&transaction->t_handle_lock);
@@ -741,6 +743,12 @@ done:
                source = kmap_atomic(page, KM_USER0);
                memcpy(jh->b_frozen_data, source+offset, jh2bh(jh)->b_size);
                kunmap_atomic(source, KM_USER0);
+
+               /*
+                * Now that the frozen data is saved off, we need to store
+                * any matching triggers.
+                */
+               jh->b_frozen_triggers = jh->b_triggers;
        }
        jbd_unlock_bh_state(bh);
 
@@ -943,6 +951,47 @@ out:
        return err;
 }
 
+/**
+ * void jbd2_journal_set_triggers() - Add triggers for commit writeout
+ * @bh: buffer to trigger on
+ * @type: struct jbd2_buffer_trigger_type containing the trigger(s).
+ *
+ * Set any triggers on this journal_head.  This is always safe, because
+ * triggers for a committing buffer will be saved off, and triggers for
+ * a running transaction will match the buffer in that transaction.
+ *
+ * Call with NULL to clear the triggers.
+ */
+void jbd2_journal_set_triggers(struct buffer_head *bh,
+                              struct jbd2_buffer_trigger_type *type)
+{
+       struct journal_head *jh = bh2jh(bh);
+
+       jh->b_triggers = type;
+}
+
+void jbd2_buffer_commit_trigger(struct journal_head *jh, void *mapped_data,
+                               struct jbd2_buffer_trigger_type *triggers)
+{
+       struct buffer_head *bh = jh2bh(jh);
+
+       if (!triggers || !triggers->t_commit)
+               return;
+
+       triggers->t_commit(triggers, bh, mapped_data, bh->b_size);
+}
+
+void jbd2_buffer_abort_trigger(struct journal_head *jh,
+                              struct jbd2_buffer_trigger_type *triggers)
+{
+       if (!triggers || !triggers->t_abort)
+               return;
+
+       triggers->t_abort(triggers, jh2bh(jh));
+}
+
+
+
 /**
  * int jbd2_journal_dirty_metadata() -  mark a buffer as containing dirty metadata
  * @handle: transaction to add buffer to.
@@ -1193,7 +1242,7 @@ int jbd2_journal_stop(handle_t *handle)
 {
        transaction_t *transaction = handle->h_transaction;
        journal_t *journal = transaction->t_journal;
-       int old_handle_count, err;
+       int err;
        pid_t pid;
 
        J_ASSERT(journal_current_handle() == handle);
@@ -1216,24 +1265,54 @@ int jbd2_journal_stop(handle_t *handle)
        /*
         * Implement synchronous transaction batching.  If the handle
         * was synchronous, don't force a commit immediately.  Let's
-        * yield and let another thread piggyback onto this transaction.
-        * Keep doing that while new threads continue to arrive.
-        * It doesn't cost much - we're about to run a commit and sleep
-        * on IO anyway.  Speeds up many-threaded, many-dir operations
-        * by 30x or more...
+        * yield and let another thread piggyback onto this
+        * transaction.  Keep doing that while new threads continue to
+        * arrive.  It doesn't cost much - we're about to run a commit
+        * and sleep on IO anyway.  Speeds up many-threaded, many-dir
+        * operations by 30x or more...
         *
-        * But don't do this if this process was the most recent one to
-        * perform a synchronous write.  We do this to detect the case where a
-        * single process is doing a stream of sync writes.  No point in waiting
-        * for joiners in that case.
+        * We try and optimize the sleep time against what the
+        * underlying disk can do, instead of having a static sleep
+        * time.  This is useful for the case where our storage is so
+        * fast that it is more optimal to go ahead and force a flush
+        * and wait for the transaction to be committed than it is to
+        * wait for an arbitrary amount of time for new writers to
+        * join the transaction.  We achieve this by measuring how
+        * long it takes to commit a transaction, and compare it with
+        * how long this transaction has been running, and if run time
+        * < commit time then we sleep for the delta and commit.  This
+        * greatly helps super fast disks that would see slowdowns as
+        * more threads started doing fsyncs.
+        *
+        * But don't do this if this process was the most recent one
+        * to perform a synchronous write.  We do this to detect the
+        * case where a single process is doing a stream of sync
+        * writes.  No point in waiting for joiners in that case.
         */
        pid = current->pid;
        if (handle->h_sync && journal->j_last_sync_writer != pid) {
+               u64 commit_time, trans_time;
+
                journal->j_last_sync_writer = pid;
-               do {
-                       old_handle_count = transaction->t_handle_count;
-                       schedule_timeout_uninterruptible(1);
-               } while (old_handle_count != transaction->t_handle_count);
+
+               spin_lock(&journal->j_state_lock);
+               commit_time = journal->j_average_commit_time;
+               spin_unlock(&journal->j_state_lock);
+
+               trans_time = ktime_to_ns(ktime_sub(ktime_get(),
+                                                  transaction->t_start_time));
+
+               commit_time = max_t(u64, commit_time,
+                                   1000*journal->j_min_batch_time);
+               commit_time = min_t(u64, commit_time,
+                                   1000*journal->j_max_batch_time);
+
+               if (trans_time < commit_time) {
+                       ktime_t expires = ktime_add_ns(ktime_get(),
+                                                      commit_time);
+                       set_current_state(TASK_UNINTERRUPTIBLE);
+                       schedule_hrtimeout(&expires, HRTIMER_MODE_ABS);
+               }
        }
 
        current->journal_info = NULL;