This patch adds supporting functions and modifies localalloc code to implement
discontiguous localalloc bitmap.

Signed-off-by: Srinivas Eeda <srinivas.e...@oracle.com>
---
 fs/ocfs2/localalloc.c |  523 ++++++++++++++++++++++++++++++++-----------------
 1 files changed, 342 insertions(+), 181 deletions(-)

diff --git a/fs/ocfs2/localalloc.c b/fs/ocfs2/localalloc.c
index 4190e53..f63381e 100644
--- a/fs/ocfs2/localalloc.c
+++ b/fs/ocfs2/localalloc.c
@@ -48,6 +48,9 @@
 
 #define OCFS2_LOCAL_ALLOC(dinode)      (&((dinode)->id2.i_lab))
 
+/* defines minimum contiguous required */
+#define OCFS2_LOCAL_ALLOC_MIN_BITS     2
+
 #define OCFS2_LOCAL_ALLOC_REC_SZ(la)   (le16_to_cpu(la->la_rec_count) *\
                                         sizeof(struct ocfs2_local_alloc_rec))
 #define OCFS2_LOCAL_ALLOC_BITMAP(la)    ((char *)(&(la->la_recs)) +\
@@ -58,7 +61,8 @@
 #define OCFS2_MAX_LOCAL_ALLOC_REC_LIMIT        128
 
 
-static u32 ocfs2_local_alloc_count_bits(struct ocfs2_dinode *alloc);
+static u32 ocfs2_local_alloc_count_bits(struct ocfs2_super *osb,
+                                       struct ocfs2_dinode *alloc);
 
 static int ocfs2_local_alloc_find_clear_bits(struct ocfs2_super *osb,
                                             struct ocfs2_dinode *alloc,
@@ -82,8 +86,7 @@ static int ocfs2_local_alloc_new_window(struct ocfs2_super 
*osb,
                                        handle_t *handle,
                                        struct ocfs2_alloc_context *ac);
 
-static int ocfs2_local_alloc_slide_window(struct ocfs2_super *osb,
-                                         struct inode *local_alloc_inode);
+static int ocfs2_local_alloc_slide_window(struct ocfs2_super *osb);
 
 /*
  * ocfs2_la_default_mb() - determine a default size, in megabytes of
@@ -202,6 +205,74 @@ unsigned int ocfs2_la_default_mb(struct ocfs2_super *osb)
        return la_mb;
 }
 
+static u32 ocfs2_local_bitmap_to_cluster(struct ocfs2_local_alloc *la, u32 bit)
+{
+       u32 start, prev, offset;
+       int rec;
+
+       rec = start = prev = 0;
+       for (rec = 0; rec < le16_to_cpu(la->la_rec_count); rec++) {
+               prev = start;
+               start += le32_to_cpu(la->la_recs[rec].la_clusters);
+               if (bit < start)
+                       break;
+       }
+       offset = le32_to_cpu(la->la_recs[rec].la_start) + (bit - prev);
+
+       return offset;
+}
+
+/*
+ * This function is called before allocating a new chunk for the localalloc
+ * bitmap to make sure there is enough space in the bitmap for the new record
+ */
+static u32 ocfs2_local_alloc_adjust_bits_wanted(struct ocfs2_local_alloc *la,
+                                               struct ocfs2_alloc_context *ac)
+{
+       u32 required, available, cluster_cnt;
+
+       if (ac->ac_bits_given == ac->ac_bits_wanted)
+               return 0;
+
+       /* total bits available in bitmap */
+       available   = le16_to_cpu(la->la_size) << 3;
+       cluster_cnt = ocfs2_local_alloc_cluster_count(la);
+
+       /*
+        * Wanted shouldn't be greater than bitmap size and given should be
+        * equal to cluster count
+        */
+       BUG_ON(ac->ac_bits_given > ac->ac_bits_wanted);
+       BUG_ON(ac->ac_bits_wanted > available);
+       BUG_ON(ac->ac_bits_given != cluster_cnt);
+
+       /* reduce bits taken by each record structure */
+       available -= (le16_to_cpu(la->la_rec_count) *
+                     OCFS2_LOCAL_ALLOC_BITS_PER_REC);
+
+       /* reduce space reserved for bitmap for already allocated clusters */
+       available -= cluster_cnt;
+
+       /* if available bits are not enough to fit a new record return 0 */
+       if (available < (OCFS2_LOCAL_ALLOC_BITS_PER_REC + 1))
+               return 0;
+
+       /* Adjust space that will be consumed by new record structure */
+       available -= OCFS2_LOCAL_ALLOC_BITS_PER_REC;
+
+       required = ac->ac_bits_wanted - ac->ac_bits_given;
+
+       /*
+        * we can't allocate clusters more than the bits available. Adjust
+        * bits wanted
+        */
+       if (required > available) {
+               ac->ac_bits_wanted = ac->ac_bits_given + available;
+               return available;
+       } else
+               return required;
+}
+
 void ocfs2_la_set_sizes(struct ocfs2_super *osb, int requested_mb)
 {
        struct super_block *sb = osb->sb;
@@ -239,12 +310,14 @@ 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);
+       if (osb->local_alloc_state == OCFS2_LA_DISABLED) {
+               cancel_delayed_work(&osb->la_enable_wq);
+               if (num_clusters >= osb->local_alloc_bits)
+                       osb->local_alloc_state = OCFS2_LA_THROTTLED;
+
+               if (num_clusters >= osb->local_alloc_default_bits)
                        osb->local_alloc_state = OCFS2_LA_ENABLED;
-               }
+       }
        spin_unlock(&osb->osb_lock);
 }
 
@@ -280,7 +353,7 @@ int ocfs2_alloc_should_use_local(struct ocfs2_super *osb, 
u64 bits)
         * a new block group. We want to be sure block group
         * allocations go through the local alloc, so allow an
         * allocation to take up to half the bitmap. */
-       if (bits > (la_bits / 2))
+       if ((la_bits > OCFS2_LOCAL_ALLOC_MIN_BITS) && (bits > (la_bits / 2)))
                goto bail;
 
        ret = 1;
@@ -348,21 +421,21 @@ int ocfs2_load_local_alloc(struct ocfs2_super *osb)
        }
 
        /* do a little verification. */
-       num_used = ocfs2_local_alloc_count_bits(alloc);
+       num_used = ocfs2_local_alloc_count_bits(osb, alloc);
 
        /* hopefully the local alloc has always been recovered before
         * we load it. */
        if (num_used
            || alloc->id1.bitmap1.i_used
            || alloc->id1.bitmap1.i_total
-           || la->la_bm_off)
+           || la->la_rec_count)
                mlog(ML_ERROR, "Local alloc hasn't been recovered!\n"
-                    "found = %u, set = %u, taken = %u, off = %u\n",
+                    "found = %u, set = %u, taken = %u\n",
                     num_used, le32_to_cpu(alloc->id1.bitmap1.i_used),
-                    le32_to_cpu(alloc->id1.bitmap1.i_total),
-                    OCFS2_LOCAL_ALLOC(alloc)->la_bm_off);
+                    le32_to_cpu(alloc->id1.bitmap1.i_total));
 
-       osb->local_alloc_bh = alloc_bh;
+       osb->local_alloc_bh    = alloc_bh;
+       osb->local_alloc_inode = inode;
        osb->local_alloc_state = OCFS2_LA_ENABLED;
 
 bail:
@@ -389,7 +462,6 @@ void ocfs2_shutdown_local_alloc(struct ocfs2_super *osb)
 {
        int status;
        handle_t *handle;
-       struct inode *local_alloc_inode = NULL;
        struct buffer_head *bh = NULL;
        struct buffer_head *main_bm_bh = NULL;
        struct inode *main_bm_inode = NULL;
@@ -402,16 +474,6 @@ void ocfs2_shutdown_local_alloc(struct ocfs2_super *osb)
        if (osb->local_alloc_state == OCFS2_LA_UNUSED)
                goto out;
 
-       local_alloc_inode =
-               ocfs2_get_system_file_inode(osb,
-                                           LOCAL_ALLOC_SYSTEM_INODE,
-                                           osb->slot_num);
-       if (!local_alloc_inode) {
-               status = -ENOENT;
-               mlog_errno(status);
-               goto out;
-       }
-
        osb->local_alloc_state = OCFS2_LA_DISABLED;
 
        ocfs2_resmap_uninit(&osb->osb_la_resmap);
@@ -451,13 +513,19 @@ void ocfs2_shutdown_local_alloc(struct ocfs2_super *osb)
        }
        memcpy(alloc_copy, alloc, bh->b_size);
 
-       status = ocfs2_journal_access_di(handle, INODE_CACHE(local_alloc_inode),
+       status = ocfs2_journal_access_di(handle,
+                                        INODE_CACHE(osb->local_alloc_inode),
                                         bh, OCFS2_JOURNAL_ACCESS_WRITE);
        if (status < 0) {
                mlog_errno(status);
                goto out_commit;
        }
 
+       status = ocfs2_sync_local_to_main(osb, handle, alloc_copy,
+                                         main_bm_inode, main_bm_bh);
+       if (status < 0)
+               mlog_errno(status);
+
        ocfs2_clear_local_alloc(alloc);
        ocfs2_journal_dirty(handle, bh);
 
@@ -465,11 +533,6 @@ void ocfs2_shutdown_local_alloc(struct ocfs2_super *osb)
        osb->local_alloc_bh = NULL;
        osb->local_alloc_state = OCFS2_LA_UNUSED;
 
-       status = ocfs2_sync_local_to_main(osb, handle, alloc_copy,
-                                         main_bm_inode, main_bm_bh);
-       if (status < 0)
-               mlog_errno(status);
-
 out_commit:
        ocfs2_commit_trans(osb, handle);
 
@@ -483,9 +546,6 @@ out_mutex:
        iput(main_bm_inode);
 
 out:
-       if (local_alloc_inode)
-               iput(local_alloc_inode);
-
        if (alloc_copy)
                kfree(alloc_copy);
 }
@@ -641,22 +701,11 @@ int ocfs2_reserve_local_alloc_bits(struct ocfs2_super 
*osb,
 {
        int status;
        struct ocfs2_dinode *alloc;
-       struct inode *local_alloc_inode;
        unsigned int free_bits;
 
        BUG_ON(!ac);
 
-       local_alloc_inode =
-               ocfs2_get_system_file_inode(osb,
-                                           LOCAL_ALLOC_SYSTEM_INODE,
-                                           osb->slot_num);
-       if (!local_alloc_inode) {
-               status = -ENOENT;
-               mlog_errno(status);
-               goto bail;
-       }
-
-       mutex_lock(&local_alloc_inode->i_mutex);
+       mutex_lock(&osb->local_alloc_inode->i_mutex);
 
        /*
         * We must double check state and allocator bits because
@@ -675,12 +724,12 @@ int ocfs2_reserve_local_alloc_bits(struct ocfs2_super 
*osb,
 
 #ifdef CONFIG_OCFS2_DEBUG_FS
        if (le32_to_cpu(alloc->id1.bitmap1.i_used) !=
-           ocfs2_local_alloc_count_bits(alloc)) {
+           ocfs2_local_alloc_count_bits(osb, alloc)) {
                ocfs2_error(osb->sb, "local alloc inode %llu says it has "
                            "%u free bits, but a count shows %u",
                            (unsigned long long)le64_to_cpu(alloc->i_blkno),
                            le32_to_cpu(alloc->id1.bitmap1.i_used),
-                           ocfs2_local_alloc_count_bits(alloc));
+                           ocfs2_local_alloc_count_bits(osb, alloc));
                status = -EIO;
                goto bail;
        }
@@ -690,8 +739,7 @@ int ocfs2_reserve_local_alloc_bits(struct ocfs2_super *osb,
                le32_to_cpu(alloc->id1.bitmap1.i_used);
        if (bits_wanted > free_bits) {
                /* uhoh, window change time. */
-               status =
-                       ocfs2_local_alloc_slide_window(osb, local_alloc_inode);
+               status = ocfs2_local_alloc_slide_window(osb);
                if (status < 0) {
                        if (status != -ENOSPC)
                                mlog_errno(status);
@@ -714,7 +762,7 @@ int ocfs2_reserve_local_alloc_bits(struct ocfs2_super *osb,
                        goto bail;
        }
 
-       ac->ac_inode = local_alloc_inode;
+       ac->ac_inode = osb->local_alloc_inode;
        /* We should never use localalloc from another slot */
        ac->ac_alloc_slot = osb->slot_num;
        ac->ac_which = OCFS2_AC_USE_LOCAL;
@@ -722,9 +770,8 @@ int ocfs2_reserve_local_alloc_bits(struct ocfs2_super *osb,
        ac->ac_bh = osb->local_alloc_bh;
        status = 0;
 bail:
-       if (status < 0 && local_alloc_inode) {
-               mutex_unlock(&local_alloc_inode->i_mutex);
-               iput(local_alloc_inode);
+       if (status < 0 && osb->local_alloc_inode) {
+               mutex_unlock(&osb->local_alloc_inode->i_mutex);
        }
 
        trace_ocfs2_reserve_local_alloc_bits(
@@ -745,7 +792,7 @@ int ocfs2_claim_local_alloc_bits(struct ocfs2_super *osb,
 {
        int status, start;
        struct inode *local_alloc_inode;
-       void *bitmap;
+       u8 *bitmap;
        struct ocfs2_dinode *alloc;
        struct ocfs2_local_alloc *la;
 
@@ -764,8 +811,8 @@ int ocfs2_claim_local_alloc_bits(struct ocfs2_super *osb,
                goto bail;
        }
 
-       bitmap = la->la_bitmap;
-       *bit_off = le32_to_cpu(la->la_bm_off) + start;
+       bitmap = OCFS2_LOCAL_ALLOC_BITMAP(la);
+       *bit_off = ocfs2_local_bitmap_to_cluster(la, start);
        *num_bits = bits_wanted;
 
        status = ocfs2_journal_access_di(handle,
@@ -792,16 +839,29 @@ bail:
        return status;
 }
 
-static u32 ocfs2_local_alloc_count_bits(struct ocfs2_dinode *alloc)
+static u32 ocfs2_local_alloc_count_bits(struct ocfs2_super *osb,
+                                       struct ocfs2_dinode *alloc)
 {
        int i;
-       u8 *buffer;
+       u8 *bitmap;
        u32 count = 0;
        struct ocfs2_local_alloc *la = OCFS2_LOCAL_ALLOC(alloc);
 
-       buffer = la->la_bitmap;
-       for (i = 0; i < le16_to_cpu(la->la_size); i++)
-               count += hweight8(buffer[i]);
+       /*
+        * if discontig is not enabled then lets update the first localalloc
+        * record with the current bitmap block info. We are doing this because
+        * old disk formats are not aware of the records.
+        */
+       if (!ocfs2_supports_discontig_la(osb) && la->la_bm_off) {
+               la->la_rec_count = cpu_to_le16(1);
+               la->la_recs[0].la_start = la->la_bm_off;
+               la->la_recs[0].la_clusters = alloc->id1.bitmap1.i_total;
+       }
+
+       bitmap = OCFS2_LOCAL_ALLOC_BITMAP(la);
+       for (i = 0; i < le32_to_cpu(alloc->id1.bitmap1.i_total); i++)
+               if (ocfs2_test_bit(i, bitmap))
+                       count++;
 
        trace_ocfs2_local_alloc_count_bits(count);
        return count;
@@ -812,10 +872,11 @@ static int ocfs2_local_alloc_find_clear_bits(struct 
ocfs2_super *osb,
                                     u32 *numbits,
                                     struct ocfs2_alloc_reservation *resv)
 {
-       int numfound, bitoff, left, startoff, lastzero;
-       int local_resv = 0;
+       int numfound, bitoff, left, startoff;
+       int i, local_resv = 0;
        struct ocfs2_alloc_reservation r;
-       void *bitmap = NULL;
+       struct ocfs2_local_alloc *la;
+       u8 *bitmap = NULL;
        struct ocfs2_reservation_map *resmap = &osb->osb_la_resmap;
 
        if (!alloc->id1.bitmap1.i_total) {
@@ -847,37 +908,44 @@ static int ocfs2_local_alloc_find_clear_bits(struct 
ocfs2_super *osb,
         * Reservations are disabled. Handle this the old way.
         */
 
-       bitmap = OCFS2_LOCAL_ALLOC(alloc)->la_bitmap;
+       la     = OCFS2_LOCAL_ALLOC(alloc);
+       bitmap = OCFS2_LOCAL_ALLOC_BITMAP(la);
 
-       numfound = bitoff = startoff = 0;
-       lastzero = -1;
-       left = le32_to_cpu(alloc->id1.bitmap1.i_total);
-       while ((bitoff = ocfs2_find_next_zero_bit(bitmap, left, startoff)) != 
-1) {
-               if (bitoff == left) {
-                       /* mlog(0, "bitoff (%d) == left", bitoff); */
-                       break;
-               }
-               /* mlog(0, "Found a zero: bitoff = %d, startoff = %d, "
-                  "numfound = %d\n", bitoff, startoff, numfound);*/
-
-               /* Ok, we found a zero bit... is it contig. or do we
-                * start over?*/
-               if (bitoff == startoff) {
-                       /* we found a zero */
-                       numfound++;
-                       startoff++;
-               } else {
-                       /* got a zero after some ones */
-                       numfound = 1;
-                       startoff = bitoff+1;
-               }
-               /* we got everything we needed */
-               if (numfound == *numbits) {
-                       /* mlog(0, "Found it all!\n"); */
-                       break;
+       left = numfound = bitoff = startoff = 0;
+       for (i = 0; i < le16_to_cpu(la->la_rec_count); i++) {
+
+               numfound  = 0;
+               startoff += left;
+               left      = le32_to_cpu(la->la_recs[i].la_clusters);
+
+               while ((bitoff = ocfs2_find_next_zero_bit(bitmap, left,
+                                                         startoff)) != -1) {
+                       if (bitoff == left) {
+                               /* mlog(0, "bitoff (%d) == left", bitoff); */
+                               break;
+                       }
+                       /* mlog(0, "Found a zero: bitoff = %d, startoff = %d, "
+                        * "numfound = %d\n", bitoff, startoff, numfound);*/
+
+                       /* Ok, we found a zero bit... is it contig. or do we
+                        * start over?*/
+                       if (bitoff == startoff) {
+                               /* we found a zero */
+                               numfound++;
+                               startoff++;
+                       } else {
+                               /* got a zero after some ones */
+                               numfound = 1;
+                               startoff = bitoff+1;
+                       }
+                       /* we got everything we needed */
+                       if (numfound == *numbits) {
+                               /* mlog(0, "Found it all!\n"); */
+                               goto out;
+                       }
                }
        }
-
+out:
        trace_ocfs2_local_alloc_find_clear_bits_search_bitmap(bitoff, numfound);
 
        if (numfound == *numbits)
@@ -900,12 +968,18 @@ static void ocfs2_clear_local_alloc(struct ocfs2_dinode 
*alloc)
 {
        struct ocfs2_local_alloc *la = OCFS2_LOCAL_ALLOC(alloc);
        int i;
+       u8 *bitmap;
 
        alloc->id1.bitmap1.i_total = 0;
        alloc->id1.bitmap1.i_used = 0;
+       la->la_rec_count = 0;
        la->la_bm_off = 0;
+
+       /* We reset the rec count so following will clear records as well */
+       bitmap = OCFS2_LOCAL_ALLOC_BITMAP(la);
+       bitmap += sizeof(struct ocfs2_local_alloc);
        for(i = 0; i < le16_to_cpu(la->la_size); i++)
-               la->la_bitmap[i] = 0;
+               bitmap[i] = 0;
 }
 
 #if 0
@@ -933,17 +1007,64 @@ static void ocfs2_verify_zero_bits(unsigned long *bitmap,
  * assumes you've already locked the main bitmap -- the bitmap inode
  * passed is used for caching.
  */
+static int ocfs2_sync_local_rec_to_main(struct ocfs2_super *osb,
+                                       handle_t *handle,
+                                       struct ocfs2_dinode *alloc,
+                                       struct inode *main_bm_inode,
+                                       struct buffer_head *main_bm_bh,
+                                       u8 *bitmap, u64 la_start_blk,
+                                       int start, int left)
+{
+       int bit_off = 0, status = 0, prev, count;
+       u64 blkno;
+
+       prev = start;
+       count = 0;
+       while ((bit_off = ocfs2_find_next_zero_bit(bitmap, left,
+                                                  start)) != -1) {
+               if ((bit_off < left) && (bit_off == start)) {
+                       count++;
+                       start++;
+                       continue;
+               }
+               if (count) {
+                       blkno = la_start_blk +
+                               ocfs2_clusters_to_blocks(osb->sb,
+                                                (start - prev) - count);
+                       mlog(0, "\nfreeing %u bits starting at local "
+                            "alloc bit %u (la_start_blk = %llu, "
+                            "blkno = %llu)\n",
+                            count, ((start - prev) - count),
+                            (unsigned long long)la_start_blk,
+                            (unsigned long long)blkno);
+                       status = ocfs2_release_clusters(handle, main_bm_inode,
+                                                       main_bm_bh, blkno,
+                                                       count);
+                       if (status < 0) {
+                               mlog_errno(status);
+                               goto bail;
+                       }
+               }
+               if (bit_off >= left)
+                       break;
+               count = 1;
+               start = bit_off + 1;
+       }
+bail:
+       return status;
+}
+
 static int ocfs2_sync_local_to_main(struct ocfs2_super *osb,
                                    handle_t *handle,
                                    struct ocfs2_dinode *alloc,
                                    struct inode *main_bm_inode,
                                    struct buffer_head *main_bm_bh)
 {
-       int status = 0;
-       int bit_off, left, count, start;
+       int i, status = 0;
+       int total, start, rec_cnt, credits;
+       u32 clusters;
        u64 la_start_blk;
-       u64 blkno;
-       void *bitmap;
+       u8 *bitmap;
        struct ocfs2_local_alloc *la = OCFS2_LOCAL_ALLOC(alloc);
 
        trace_ocfs2_sync_local_to_main(
@@ -954,49 +1075,58 @@ static int ocfs2_sync_local_to_main(struct ocfs2_super 
*osb,
                goto bail;
        }
 
+       /* if all bits are used nothing to sync, just return */
        if (le32_to_cpu(alloc->id1.bitmap1.i_used) ==
            le32_to_cpu(alloc->id1.bitmap1.i_total)) {
                goto bail;
        }
 
-       la_start_blk = ocfs2_clusters_to_blocks(osb->sb,
-                                               le32_to_cpu(la->la_bm_off));
-       bitmap = la->la_bitmap;
-       start = count = bit_off = 0;
-       left = le32_to_cpu(alloc->id1.bitmap1.i_total);
+       bitmap  = OCFS2_LOCAL_ALLOC_BITMAP(la);
+       rec_cnt = le16_to_cpu(la->la_rec_count) - 1;
 
-       while ((bit_off = ocfs2_find_next_zero_bit(bitmap, left, start))
-              != -1) {
-               if ((bit_off < left) && (bit_off == start)) {
-                       count++;
-                       start++;
-                       continue;
-               }
-               if (count) {
-                       blkno = la_start_blk +
-                               ocfs2_clusters_to_blocks(osb->sb,
-                                                        start - count);
+       for (i = rec_cnt; i >= 0 ; i--) {
+               la_start_blk = ocfs2_clusters_to_blocks(osb->sb,
+                                       le32_to_cpu(la->la_recs[i].la_start));
 
-                       trace_ocfs2_sync_local_to_main_free(
-                            count, start - count,
-                            (unsigned long long)la_start_blk,
-                            (unsigned long long)blkno);
+               total    = le32_to_cpu(alloc->id1.bitmap1.i_total);
+               clusters = le32_to_cpu(la->la_recs[i].la_clusters);
+               start    = total - clusters;
 
-                       status = ocfs2_release_clusters(handle,
-                                                       main_bm_inode,
-                                                       main_bm_bh, blkno,
-                                                       count);
+               status = ocfs2_sync_local_rec_to_main(osb, handle, alloc,
+                                                     main_bm_inode,
+                                                     main_bm_bh, bitmap,
+                                                     la_start_blk, start,
+                                                     total);
+               if (status < 0) {
+                       mlog_errno(status);
+                       goto bail;
+               }
+               la->la_bm_off              = 0;
+               la->la_recs[i].la_start    = 0;
+               la->la_recs[i].la_clusters = 0;
+               le16_add_cpu(&la->la_rec_count, -1);
+               le32_add_cpu(&alloc->id1.bitmap1.i_total, -clusters);
+
+               ocfs2_journal_dirty(handle, osb->local_alloc_bh);
+
+               /* if we need more credits extend the transaction */
+               credits = OCFS2_WINDOW_MOVE_CREDITS - handle->h_buffer_credits;
+               if (credits > 0) {
+                       status = ocfs2_extend_trans(handle, credits);
+                       if (status < 0) {
+                               mlog_errno(status);
+                               goto bail;
+                       }
+                       status = ocfs2_journal_access_di(handle,
+                                        INODE_CACHE(osb->local_alloc_inode),
+                                        osb->local_alloc_bh,
+                                        OCFS2_JOURNAL_ACCESS_WRITE);
                        if (status < 0) {
                                mlog_errno(status);
                                goto bail;
                        }
                }
-               if (bit_off >= left)
-                       break;
-               count = 1;
-               start = bit_off + 1;
        }
-
 bail:
        if (status)
                mlog_errno(status);
@@ -1046,9 +1176,12 @@ static int ocfs2_recalc_la_window(struct ocfs2_super 
*osb,
                 * 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.
+                * In general we will be seeing atleast few contiguous free
+                * bits. It should be ok to keep local alloc enabled even
+                * in extreme case where max available contiguous free bit is 1
                 */
                bits = osb->local_alloc_bits >> 1;
-               if (bits > ocfs2_megabytes_to_clusters(osb->sb, 1)) {
+               if (bits) {
                        /*
                         * By setting state to THROTTLED, we'll keep
                         * the number of local alloc bits used down
@@ -1096,8 +1229,9 @@ static int ocfs2_local_alloc_reserve_for_window(struct 
ocfs2_super *osb,
                goto bail;
        }
 
+       osb->local_alloc_bits = osb->local_alloc_default_bits;
 retry_enospc:
-       (*ac)->ac_bits_wanted = osb->local_alloc_default_bits;
+       (*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) ==
@@ -1137,9 +1271,11 @@ static int ocfs2_local_alloc_new_window(struct 
ocfs2_super *osb,
                                        struct ocfs2_alloc_context *ac)
 {
        int status = 0;
-       u32 cluster_off, cluster_count;
+       u32 wanted, cluster_off, cluster_count;
        struct ocfs2_dinode *alloc = NULL;
        struct ocfs2_local_alloc *la;
+       u8 *bitmap;
+       int i, rec_cnt, credits;
 
        alloc = (struct ocfs2_dinode *) osb->local_alloc_bh->b_data;
        la = OCFS2_LOCAL_ALLOC(alloc);
@@ -1156,72 +1292,97 @@ static int ocfs2_local_alloc_new_window(struct 
ocfs2_super *osb,
        /* we used the generic suballoc reserve function, but we set
         * everything up nicely, so there's no reason why we can't use
         * the more specific cluster api to claim bits. */
-       status = ocfs2_claim_clusters(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;
-
-               ac->ac_bits_wanted = osb->local_alloc_default_bits;
-               status = ocfs2_claim_clusters(handle, ac,
-                                             osb->local_alloc_bits,
-                                             &cluster_off,
+       rec_cnt = 0;
+       wanted = osb->local_alloc_bits;
+       while (1) {
+               status = ocfs2_claim_clusters(handle, ac, wanted, &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 == -ENOSPC) {
+                       /* reduce window size and retry */
+                       if (ocfs2_recalc_la_window(osb,
+                          OCFS2_LA_EVENT_FRAGMENTED) == OCFS2_LA_DISABLED)
+                               break;
+                       wanted = osb->local_alloc_bits;
+                       continue;
+               } else if (status < 0)
+                       break;
+
+               BUG_ON(ac->ac_bits_given > ac->ac_bits_wanted);
+
+               /* found a window */
+               la->la_recs[rec_cnt].la_start    = cpu_to_le32(cluster_off);
+               la->la_recs[rec_cnt].la_clusters = cpu_to_le32(cluster_count);
+               rec_cnt++;
+               la->la_rec_count = cpu_to_le16(rec_cnt);
+               le32_add_cpu(&alloc->id1.bitmap1.i_total, cluster_count);
+
+               ocfs2_journal_dirty(handle, osb->local_alloc_bh);
+
+               if (!ocfs2_supports_discontig_la(osb)) {
+                       la->la_bm_off = cpu_to_le32(cluster_off);
+                       break;
+               }
+
+               /* exit if we can't fit another record */
+               wanted = ocfs2_local_alloc_adjust_bits_wanted(la, ac);
+               if (!wanted)
+                       break;
+
+               if (wanted > osb->local_alloc_bits)
+                       wanted = osb->local_alloc_bits;
+
+               /* if we need more credits extend the transaction */
+               if (rec_cnt >= OCFS2_MAX_LOCAL_ALLOC_REC_LIMIT)
+                       break;
+
+               credits = OCFS2_WINDOW_MOVE_CREDITS - handle->h_buffer_credits;
+               if (credits > 0) {
+                       status = ocfs2_extend_trans(handle, credits);
+                       if (status < 0) {
+                               mlog_errno(status);
+                               goto bail;
+                       }
+                       status = ocfs2_journal_access_di(handle,
+                                        INODE_CACHE(osb->local_alloc_inode),
+                                        osb->local_alloc_bh,
+                                        OCFS2_JOURNAL_ACCESS_WRITE);
+                       if (status < 0) {
+                               mlog_errno(status);
+                               goto bail;
+                       }
                }
        }
-       if (status < 0) {
-               if (status != -ENOSPC)
-                       mlog_errno(status);
+       if (!rec_cnt)
                goto bail;
-       }
 
+       osb->local_alloc_state = OCFS2_LA_ENABLED;
+       spin_lock(&osb->osb_lock);
+       if (cluster_count > osb->local_alloc_bits)
+               osb->local_alloc_bits = cluster_count;
+       spin_unlock(&osb->osb_lock);
        osb->la_last_gd = ac->ac_last_group;
 
-       la->la_bm_off = cpu_to_le32(cluster_off);
-       alloc->id1.bitmap1.i_total = cpu_to_le32(cluster_count);
-       /* just in case... In the future when we find space ourselves,
-        * we don't have to get all contiguous -- but we'll have to
-        * set all previously used bits in bitmap and update
-        * la_bits_set before setting the bits in the main bitmap. */
-       alloc->id1.bitmap1.i_used = 0;
-       memset(OCFS2_LOCAL_ALLOC(alloc)->la_bitmap, 0,
-              le16_to_cpu(la->la_size));
-
-       ocfs2_resmap_restart(&osb->osb_la_resmap, cluster_count,
-                            OCFS2_LOCAL_ALLOC(alloc)->la_bitmap);
+       bitmap = OCFS2_LOCAL_ALLOC_BITMAP(la);
+       ocfs2_resmap_restart(&osb->osb_la_resmap, rec_cnt,
+                            alloc->id1.bitmap1.i_total, bitmap);
+       for (i = 0; i < rec_cnt; i++)
+               ocfs2_resmap_set_ext(&osb->osb_la_resmap, i,
+                                    le32_to_cpu(la->la_recs[i].la_clusters));
 
-       trace_ocfs2_local_alloc_new_window_result(
-               OCFS2_LOCAL_ALLOC(alloc)->la_bm_off,
+       trace_ocfs2_local_alloc_new_window_result
+               (OCFS2_LOCAL_ALLOC(alloc)->la_recs[0].la_start,
                le32_to_cpu(alloc->id1.bitmap1.i_total));
 
 bail:
-       if (status)
+       if ((status < 0) && (status != -ENOSPC))
                mlog_errno(status);
+
        return status;
 }
 
 /* Note that we do *NOT* lock the local alloc inode here as
  * it's been locked already for us. */
-static int ocfs2_local_alloc_slide_window(struct ocfs2_super *osb,
-                                         struct inode *local_alloc_inode)
+static int ocfs2_local_alloc_slide_window(struct ocfs2_super *osb)
 {
        int status = 0;
        struct buffer_head *main_bm_bh = NULL;
@@ -1268,7 +1429,7 @@ static int ocfs2_local_alloc_slide_window(struct 
ocfs2_super *osb,
        memcpy(alloc_copy, alloc, osb->local_alloc_bh->b_size);
 
        status = ocfs2_journal_access_di(handle,
-                                        INODE_CACHE(local_alloc_inode),
+                                        INODE_CACHE(osb->local_alloc_inode),
                                         osb->local_alloc_bh,
                                         OCFS2_JOURNAL_ACCESS_WRITE);
        if (status < 0) {
-- 
1.5.4.3


_______________________________________________
Ocfs2-devel mailing list
Ocfs2-devel@oss.oracle.com
http://oss.oracle.com/mailman/listinfo/ocfs2-devel

Reply via email to