]> git.neil.brown.name Git - LaFS.git/blobdiff - cluster.c
Fix lafs_seg_next to talk write-cluster properly.
[LaFS.git] / cluster.c
index 5e188ce6231f093e0ee5dc2e73f92e63bb10ca2e..a76b0f5b9037007dc7f07149fbaedfe22fdde702 100644 (file)
--- a/cluster.c
+++ b/cluster.c
@@ -340,6 +340,13 @@ static u64 seg_addr(struct fs *fs, struct segpos *seg)
                /* Setting 'next' address for last cluster in
                 * a cleaner segment */
                return 0;
+       if (seg->table > seg->nxt_table ||
+           (seg->table == seg->nxt_table &&
+            seg->row >= seg->nxt_row))
+               /* We have gone beyond the end of the cluster,
+                * must be bad data during roll-forward
+                */
+               return 0;
        addr = seg->col * dv->stride;
        addr += seg->row;
        addr += seg->table * dv->rows_per_table;
@@ -352,19 +359,38 @@ u64 lafs_seg_next(struct fs *fs, struct segpos *seg)
 {
        /* step forward one block, returning the address of
         * the block stepped over
+        * The write cluster can have three sections:
+        * - the tail end of one table
+        * - a number of complete tables
+        * - the head of one table
+        *
+        * For each table or partial table we want to talk a full
+        * column at a time, then go to the next column.
+        * So we step to the next row.  If it is beyond the range of
+        * the current table, go to 'start' of next column, or to
+        * next table.
         */
        struct fs_dev *dv = &fs->devs[seg->dev];
        u64 addr = seg_addr(fs, seg);
 
+       if (!addr)
+               /* Beyond end of cluster */
+               return 0;
+
        /* now step forward in column or table or seg */
-       seg->col++;
-       if (seg->col >= dv->width) {
-               seg->col = 0;
-               seg->row++;
-               if (seg->row >= dv->rows_per_table) {
-                       seg->row = 0;
+       seg->row++;
+       if (seg->row >= dv->rows_per_table ||
+           (seg->table == seg->nxt_table
+            && seg->row >= seg->nxt_row)) {
+               seg->col++;
+               if (seg->col >= dv->width) {
+                       seg->col = 0;
                        seg->table++;
                }
+               if (seg->table == seg->st_table)
+                       seg->row = seg->st_row;
+               else
+                       seg->row = 0;
        }
        return addr;
 }
@@ -1176,6 +1202,7 @@ static void cluster_flush(struct fs *fs, int cnum)
                        current_block++;
                cluster_incdesc(wc, desc_start, b, fs->blocksize_bits);
                addr = lafs_seg_next(fs, &wc->seg);
+               BUG_ON(!addr);
                if (cnum && test_bit(B_Dirty, &b->flags))
                        /* We are cleaning but this block is now dirty.
                         * Don't waste the UnincCredit on recording the