]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - fs/ocfs2/localalloc.c
ocfs2: throttle back local alloc when low on disk space
[linux-2.6-omap-h63xx.git] / fs / ocfs2 / localalloc.c
index b05ce6642919917225198c45372148e8e3aaca35..f71658adddb51c7e7a62b5bdf99eb6d4f3bac01b 100644 (file)
@@ -73,16 +73,51 @@ static int ocfs2_local_alloc_new_window(struct ocfs2_super *osb,
 static int ocfs2_local_alloc_slide_window(struct ocfs2_super *osb,
                                          struct inode *local_alloc_inode);
 
+static inline int ocfs2_la_state_enabled(struct ocfs2_super *osb)
+{
+       return (osb->local_alloc_state == OCFS2_LA_THROTTLED ||
+               osb->local_alloc_state == OCFS2_LA_ENABLED);
+}
+
+void ocfs2_local_alloc_seen_free_bits(struct ocfs2_super *osb,
+                                     unsigned int num_clusters)
+{
+       spin_lock(&osb->osb_lock);
+       if (osb->local_alloc_state == OCFS2_LA_DISABLED ||
+           osb->local_alloc_state == OCFS2_LA_THROTTLED)
+               if (num_clusters >= osb->local_alloc_default_bits) {
+                       cancel_delayed_work(&osb->la_enable_wq);
+                       osb->local_alloc_state = OCFS2_LA_ENABLED;
+               }
+       spin_unlock(&osb->osb_lock);
+}
+
+void ocfs2_la_enable_worker(struct work_struct *work)
+{
+       struct ocfs2_super *osb =
+               container_of(work, struct ocfs2_super,
+                            la_enable_wq.work);
+       spin_lock(&osb->osb_lock);
+       osb->local_alloc_state = OCFS2_LA_ENABLED;
+       spin_unlock(&osb->osb_lock);
+}
+
 /*
  * Tell us whether a given allocation should use the local alloc
  * file. Otherwise, it has to go to the main bitmap.
+ *
+ * This function does semi-dirty reads of local alloc size and state!
+ * This is ok however, as the values are re-checked once under mutex.
  */
 int ocfs2_alloc_should_use_local(struct ocfs2_super *osb, u64 bits)
 {
-       int la_bits = osb->local_alloc_bits;
        int ret = 0;
+       int la_bits;
+
+       spin_lock(&osb->osb_lock);
+       la_bits = osb->local_alloc_bits;
 
-       if (osb->local_alloc_state != OCFS2_LA_ENABLED)
+       if (!ocfs2_la_state_enabled(osb))
                goto bail;
 
        /* la_bits should be at least twice the size (in clusters) of
@@ -96,6 +131,7 @@ int ocfs2_alloc_should_use_local(struct ocfs2_super *osb, u64 bits)
 bail:
        mlog(0, "state=%d, bits=%llu, la_bits=%d, ret=%d\n",
             osb->local_alloc_state, (unsigned long long)bits, la_bits, ret);
+       spin_unlock(&osb->osb_lock);
        return ret;
 }
 
@@ -208,6 +244,9 @@ void ocfs2_shutdown_local_alloc(struct ocfs2_super *osb)
 
        mlog_entry_void();
 
+       cancel_delayed_work(&osb->la_enable_wq);
+       flush_workqueue(ocfs2_wq);
+
        if (osb->local_alloc_state == OCFS2_LA_UNUSED)
                goto out;
 
@@ -445,7 +484,7 @@ out:
 }
 
 /*
- * make sure we've got at least bitswanted contiguous bits in the
+ * make sure we've got at least bits_wanted contiguous bits in the
  * local alloc. You lose them when you drop i_mutex.
  *
  * We will add ourselves to the transaction passed in, but may start
@@ -476,16 +515,18 @@ int ocfs2_reserve_local_alloc_bits(struct ocfs2_super *osb,
 
        mutex_lock(&local_alloc_inode->i_mutex);
 
-       if (osb->local_alloc_state != OCFS2_LA_ENABLED) {
-               status = -ENOSPC;
-               goto bail;
-       }
-
-       if (bits_wanted > osb->local_alloc_bits) {
-               mlog(0, "Asking for more than my max window size!\n");
+       /*
+        * We must double check state and allocator bits because
+        * another process may have changed them while holding i_mutex.
+        */
+       spin_lock(&osb->osb_lock);
+       if (!ocfs2_la_state_enabled(osb) ||
+           (bits_wanted > osb->local_alloc_bits)) {
+               spin_unlock(&osb->osb_lock);
                status = -ENOSPC;
                goto bail;
        }
+       spin_unlock(&osb->osb_lock);
 
        alloc = (struct ocfs2_dinode *) osb->local_alloc_bh->b_data;
 
@@ -513,6 +554,21 @@ int ocfs2_reserve_local_alloc_bits(struct ocfs2_super *osb,
                                mlog_errno(status);
                        goto bail;
                }
+
+               /*
+                * Under certain conditions, the window slide code
+                * might have reduced the number of bits available or
+                * disabled the the local alloc entirely. Re-check
+                * here and return -ENOSPC if necessary.
+                */
+               status = -ENOSPC;
+               if (!ocfs2_la_state_enabled(osb))
+                       goto bail;
+
+               free_bits = le32_to_cpu(alloc->id1.bitmap1.i_total) -
+                       le32_to_cpu(alloc->id1.bitmap1.i_used);
+               if (bits_wanted > free_bits)
+                       goto bail;
        }
 
        ac->ac_inode = local_alloc_inode;
@@ -780,6 +836,85 @@ bail:
        return status;
 }
 
+enum ocfs2_la_event {
+       OCFS2_LA_EVENT_SLIDE,           /* Normal window slide. */
+       OCFS2_LA_EVENT_FRAGMENTED,      /* The global bitmap has
+                                        * enough bits theoretically
+                                        * free, but a contiguous
+                                        * allocation could not be
+                                        * found. */
+       OCFS2_LA_EVENT_ENOSPC,          /* Global bitmap doesn't have
+                                        * enough bits free to satisfy
+                                        * our request. */
+};
+#define OCFS2_LA_ENABLE_INTERVAL (30 * HZ)
+/*
+ * Given an event, calculate the size of our next local alloc window.
+ *
+ * This should always be called under i_mutex of the local alloc inode
+ * so that local alloc disabling doesn't race with processes trying to
+ * use the allocator.
+ *
+ * Returns the state which the local alloc was left in. This value can
+ * be ignored by some paths.
+ */
+static int ocfs2_recalc_la_window(struct ocfs2_super *osb,
+                                 enum ocfs2_la_event event)
+{
+       unsigned int bits;
+       int state;
+
+       spin_lock(&osb->osb_lock);
+       if (osb->local_alloc_state == OCFS2_LA_DISABLED) {
+               WARN_ON_ONCE(osb->local_alloc_state == OCFS2_LA_DISABLED);
+               goto out_unlock;
+       }
+
+       /*
+        * ENOSPC and fragmentation are treated similarly for now.
+        */
+       if (event == OCFS2_LA_EVENT_ENOSPC ||
+           event == OCFS2_LA_EVENT_FRAGMENTED) {
+               /*
+                * We ran out of contiguous space in the primary
+                * bitmap. Drastically reduce the number of bits used
+                * by local alloc until we have to disable it.
+                */
+               bits = osb->local_alloc_bits >> 1;
+               if (bits > ocfs2_megabytes_to_clusters(osb->sb, 1)) {
+                       /*
+                        * By setting state to THROTTLED, we'll keep
+                        * the number of local alloc bits used down
+                        * until an event occurs which would give us
+                        * reason to assume the bitmap situation might
+                        * have changed.
+                        */
+                       osb->local_alloc_state = OCFS2_LA_THROTTLED;
+                       osb->local_alloc_bits = bits;
+               } else {
+                       osb->local_alloc_state = OCFS2_LA_DISABLED;
+               }
+               queue_delayed_work(ocfs2_wq, &osb->la_enable_wq,
+                                  OCFS2_LA_ENABLE_INTERVAL);
+               goto out_unlock;
+       }
+
+       /*
+        * Don't increase the size of the local alloc window until we
+        * know we might be able to fulfill the request. Otherwise, we
+        * risk bouncing around the global bitmap during periods of
+        * low space.
+        */
+       if (osb->local_alloc_state != OCFS2_LA_THROTTLED)
+               osb->local_alloc_bits = osb->local_alloc_default_bits;
+
+out_unlock:
+       state = osb->local_alloc_state;
+       spin_unlock(&osb->osb_lock);
+
+       return state;
+}
+
 static int ocfs2_local_alloc_reserve_for_window(struct ocfs2_super *osb,
                                                struct ocfs2_alloc_context **ac,
                                                struct inode **bitmap_inode,
@@ -794,12 +929,21 @@ static int ocfs2_local_alloc_reserve_for_window(struct ocfs2_super *osb,
                goto bail;
        }
 
+retry_enospc:
        (*ac)->ac_bits_wanted = osb->local_alloc_bits;
 
        status = ocfs2_reserve_cluster_bitmap_bits(osb, *ac);
+       if (status == -ENOSPC) {
+               if (ocfs2_recalc_la_window(osb, OCFS2_LA_EVENT_ENOSPC) ==
+                   OCFS2_LA_DISABLED)
+                       goto bail;
+
+               ocfs2_free_ac_resource(*ac);
+               memset(*ac, 0, sizeof(struct ocfs2_alloc_context));
+               goto retry_enospc;
+       }
        if (status < 0) {
-               if (status != -ENOSPC)
-                       mlog_errno(status);
+               mlog_errno(status);
                goto bail;
        }
 
@@ -852,6 +996,34 @@ static int ocfs2_local_alloc_new_window(struct ocfs2_super *osb,
         * the more specific cluster api to claim bits. */
        status = ocfs2_claim_clusters(osb, handle, ac, osb->local_alloc_bits,
                                      &cluster_off, &cluster_count);
+       if (status == -ENOSPC) {
+retry_enospc:
+               /*
+                * Note: We could also try syncing the journal here to
+                * allow use of any free bits which the current
+                * transaction can't give us access to. --Mark
+                */
+               if (ocfs2_recalc_la_window(osb, OCFS2_LA_EVENT_FRAGMENTED) ==
+                   OCFS2_LA_DISABLED)
+                       goto bail;
+
+               status = ocfs2_claim_clusters(osb, handle, ac,
+                                             osb->local_alloc_bits,
+                                             &cluster_off,
+                                             &cluster_count);
+               if (status == -ENOSPC)
+                       goto retry_enospc;
+               /*
+                * We only shrunk the *minimum* number of in our
+                * request - it's entirely possible that the allocator
+                * might give us more than we asked for.
+                */
+               if (status == 0) {
+                       spin_lock(&osb->osb_lock);
+                       osb->local_alloc_bits = cluster_count;
+                       spin_unlock(&osb->osb_lock);
+               }
+       }
        if (status < 0) {
                if (status != -ENOSPC)
                        mlog_errno(status);
@@ -895,6 +1067,8 @@ static int ocfs2_local_alloc_slide_window(struct ocfs2_super *osb,
 
        mlog_entry_void();
 
+       ocfs2_recalc_la_window(osb, OCFS2_LA_EVENT_SLIDE);
+
        /* This will lock the main bitmap for us. */
        status = ocfs2_local_alloc_reserve_for_window(osb,
                                                      &ac,