]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - fs/ufs/balloc.c
[PATCH] ufs: reallocation fix
[karo-tx-linux.git] / fs / ufs / balloc.c
index 95b878e5c7a077783f4971abae30cf9794c91442..638f4c585e89a404f0bb87ee5ddea0bd2cf3fc9f 100644 (file)
@@ -217,48 +217,6 @@ failed:
        return;
 }
 
-static struct page *ufs_get_locked_page(struct address_space *mapping,
-                                 unsigned long index)
-{
-       struct page *page;
-
-try_again:
-       page = find_lock_page(mapping, index);
-       if (!page) {
-               page = read_cache_page(mapping, index,
-                                      (filler_t*)mapping->a_ops->readpage,
-                                      NULL);
-               if (IS_ERR(page)) {
-                       printk(KERN_ERR "ufs_change_blocknr: "
-                              "read_cache_page error: ino %lu, index: %lu\n",
-                              mapping->host->i_ino, index);
-                       goto out;
-               }
-
-               lock_page(page);
-
-               if (!PageUptodate(page) || PageError(page)) {
-                       unlock_page(page);
-                       page_cache_release(page);
-
-                       printk(KERN_ERR "ufs_change_blocknr: "
-                              "can not read page: ino %lu, index: %lu\n",
-                              mapping->host->i_ino, index);
-
-                       page = ERR_PTR(-EIO);
-                       goto out;
-               }
-       }
-
-       if (unlikely(!page->mapping || !page_has_buffers(page))) {
-               unlock_page(page);
-               page_cache_release(page);
-               goto try_again;/*we really need these buffers*/
-       }
-out:
-       return page;
-}
-
 /*
  * Modify inode page cache in such way:
  * have - blocks with b_blocknr equal to oldb...oldb+count-1
@@ -269,56 +227,87 @@ out:
  * We can come here from ufs_writepage or ufs_prepare_write,
  * locked_page is argument of these functions, so we already lock it.
  */
-static void ufs_change_blocknr(struct inode *inode, unsigned int baseblk,
+static void ufs_change_blocknr(struct inode *inode, unsigned int beg,
                               unsigned int count, unsigned int oldb,
                               unsigned int newb, struct page *locked_page)
 {
-       unsigned int blk_per_page = 1 << (PAGE_CACHE_SHIFT - inode->i_blkbits);
-       struct address_space *mapping = inode->i_mapping;
-       pgoff_t index, cur_index = locked_page->index;
-       unsigned int i, j;
+       const unsigned mask = (1 << (PAGE_CACHE_SHIFT - inode->i_blkbits)) - 1;
+       struct address_space * const mapping = inode->i_mapping;
+       pgoff_t index, cur_index;
+       unsigned end, pos, j;
        struct page *page;
        struct buffer_head *head, *bh;
 
        UFSD("ENTER, ino %lu, count %u, oldb %u, newb %u\n",
              inode->i_ino, count, oldb, newb);
 
+       BUG_ON(!locked_page);
        BUG_ON(!PageLocked(locked_page));
 
-       for (i = 0; i < count; i += blk_per_page) {
-               index = (baseblk+i) >> (PAGE_CACHE_SHIFT - inode->i_blkbits);
+       cur_index = locked_page->index;
+
+       for (end = count + beg; beg < end; beg = (beg | mask) + 1) {
+               index = beg >> (PAGE_CACHE_SHIFT - inode->i_blkbits);
 
                if (likely(cur_index != index)) {
                        page = ufs_get_locked_page(mapping, index);
-                       if (IS_ERR(page))
+                       if (!page || IS_ERR(page)) /* it was truncated or EIO */
                                continue;
                } else
                        page = locked_page;
 
-               j = i;
                head = page_buffers(page);
                bh = head;
+               pos = beg & mask;
+               for (j = 0; j < pos; ++j)
+                       bh = bh->b_this_page;
+               j = 0;
                do {
-                       if (likely(bh->b_blocknr == j + oldb && j < count)) {
-                               unmap_underlying_metadata(bh->b_bdev,
-                                                         bh->b_blocknr);
-                               bh->b_blocknr = newb + j++;
-                               mark_buffer_dirty(bh);
+                       if (buffer_mapped(bh)) {
+                               pos = bh->b_blocknr - oldb;
+                               if (pos < count) {
+                                       UFSD(" change from %llu to %llu\n",
+                                            (unsigned long long)pos + oldb,
+                                            (unsigned long long)pos + newb);
+                                       bh->b_blocknr = newb + pos;
+                                       unmap_underlying_metadata(bh->b_bdev,
+                                                                 bh->b_blocknr);
+                                       mark_buffer_dirty(bh);
+                                       ++j;
+                               }
                        }
 
                        bh = bh->b_this_page;
                } while (bh != head);
 
-               set_page_dirty(page);
+               if (j)
+                       set_page_dirty(page);
 
-               if (likely(cur_index != index)) {
-                       unlock_page(page);
-                       page_cache_release(page);
-               }
+               if (likely(cur_index != index))
+                       ufs_put_locked_page(page);
        }
        UFSD("EXIT\n");
 }
 
+static void ufs_clear_frags(struct inode *inode, sector_t beg, unsigned int n,
+                           int sync)
+{
+       struct buffer_head *bh;
+       sector_t end = beg + n;
+
+       for (; beg < end; ++beg) {
+               bh = sb_getblk(inode->i_sb, beg);
+               lock_buffer(bh);
+               memset(bh->b_data, 0, inode->i_sb->s_blocksize);
+               set_buffer_uptodate(bh);
+               mark_buffer_dirty(bh);
+               unlock_buffer(bh);
+               if (IS_SYNC(inode) || sync)
+                       sync_dirty_buffer(bh);
+               brelse(bh);
+       }
+}
+
 unsigned ufs_new_fragments(struct inode * inode, __fs32 * p, unsigned fragment,
                           unsigned goal, unsigned count, int * err, struct page *locked_page)
 {
@@ -394,6 +383,8 @@ unsigned ufs_new_fragments(struct inode * inode, __fs32 * p, unsigned fragment,
                        *p = cpu_to_fs32(sb, result);
                        *err = 0;
                        UFS_I(inode)->i_lastfrag = max_t(u32, UFS_I(inode)->i_lastfrag, fragment + count);
+                       ufs_clear_frags(inode, result + oldcount, newcount - oldcount,
+                                       locked_page != NULL);
                }
                unlock_super(sb);
                UFSD("EXIT, result %u\n", result);
@@ -407,6 +398,8 @@ unsigned ufs_new_fragments(struct inode * inode, __fs32 * p, unsigned fragment,
        if (result) {
                *err = 0;
                UFS_I(inode)->i_lastfrag = max_t(u32, UFS_I(inode)->i_lastfrag, fragment + count);
+               ufs_clear_frags(inode, result + oldcount, newcount - oldcount,
+                               locked_page != NULL);
                unlock_super(sb);
                UFSD("EXIT, result %u\n", result);
                return result;
@@ -436,6 +429,8 @@ unsigned ufs_new_fragments(struct inode * inode, __fs32 * p, unsigned fragment,
        }
        result = ufs_alloc_fragments (inode, cgno, goal, request, err);
        if (result) {
+               ufs_clear_frags(inode, result + oldcount, newcount - oldcount,
+                               locked_page != NULL);
                ufs_change_blocknr(inode, fragment - oldcount, oldcount, tmp,
                                   result, locked_page);