]> git.hungrycats.org Git - linux/commitdiff
[XFS] cleanup delayed allocate write path a little and fix some
authorStephen Lord <lord@sgi.com>
Wed, 19 Feb 2003 19:13:48 +0000 (20:13 +0100)
committerChristoph Hellwig <hch@lab343.munich.sgi.com>
Wed, 19 Feb 2003 19:13:48 +0000 (20:13 +0100)
small bugs in there.

SGI Modid: 2.5.x-xfs:slinx:138445a

fs/xfs/linux/xfs_aops.c
fs/xfs/linux/xfs_iomap.c

index 5505483ca88ccd627e88eacef71da1ccc7542a90..0b9c47c4365b975ba74d74dd57dd1b0e7331618b 100644 (file)
@@ -53,12 +53,15 @@ map_blocks(
                count = max_t(ssize_t, count, XFS_WRITE_IO_LOG);
 retry:
        VOP_BMAP(vp, offset, count, flags, pbmapp, &nmaps, error);
-       if (flags & PBF_WRITE) {
-               if (unlikely((flags & PBF_DIRECT) && nmaps &&
-                   (pbmapp->pbm_flags & PBMF_DELAY))) {
-                       flags = PBF_FILE_ALLOCATE;
-                       goto retry;
-               }
+       if (error == EAGAIN)
+               return -error;
+       if (unlikely((flags & (PBF_WRITE|PBF_DIRECT)) ==
+                                       (PBF_WRITE|PBF_DIRECT) && nmaps &&
+                                       (pbmapp->pbm_flags & PBMF_DELAY))) {
+               flags = PBF_FILE_ALLOCATE;
+               goto retry;
+       }
+       if (flags & (PBF_WRITE|PBF_FILE_ALLOCATE)) {
                VMODIFY(vp);
        }
        return -error;
@@ -309,6 +312,7 @@ convert_page(
                if (startio && (offset < end)) {
                        bh_arr[index++] = bh;
                } else {
+                       set_buffer_dirty(bh);
                        unlock_buffer(bh);
                }
        } while (i++, (bh = bh->b_this_page) != head);
@@ -365,9 +369,9 @@ cluster_write(
 
 STATIC int
 delalloc_convert(
-       struct page             *page,
-       int                     startio,
-       int                     allocate_space)
+       struct page     *page,
+       int             startio,
+       int             unmapped) /* also implies page uptodate */
 {
        struct inode            *inode = page->mapping->host;
        struct buffer_head      *bh_arr[MAX_BUF_PER_PAGE], *bh, *head;
@@ -375,6 +379,9 @@ delalloc_convert(
        unsigned long           p_offset = 0, end_index;
        loff_t                  offset, end_offset;
        int                     len, err, i, cnt = 0, uptodate = 1;
+       int                     flags = startio ? 0 : PBF_TRYLOCK;
+       int                     page_dirty = 1;
+
 
        /* Are we off the end of the file ? */
        end_index = inode->i_size >> PAGE_CACHE_SHIFT;
@@ -390,9 +397,6 @@ delalloc_convert(
        if (end_offset > inode->i_size)
                end_offset = inode->i_size;
 
-       if (startio && !page_has_buffers(page))
-               create_empty_buffers(page, 1 << inode->i_blkbits, 0);
-
        bh = head = page_buffers(page);
        mp = NULL;
 
@@ -406,10 +410,14 @@ delalloc_convert(
                        mp = match_offset_to_mapping(page, &map, p_offset);
                }
 
+               /*
+                * First case, allocate space for delalloc buffer head
+                * we can return EAGAIN here in the release page case.
+                */
                if (buffer_delay(bh)) {
                        if (!mp) {
                                err = map_blocks(inode, offset, len, &map,
-                                               PBF_FILE_ALLOCATE);
+                                       PBF_FILE_ALLOCATE | flags);
                                if (err) {
                                        goto error;
                                }
@@ -422,14 +430,17 @@ delalloc_convert(
                                if (startio) {
                                        bh_arr[cnt++] = bh;
                                } else {
+                                       set_buffer_dirty(bh);
                                        unlock_buffer(bh);
                                }
+                               page_dirty = 0;
                        }
                } else if ((buffer_uptodate(bh) || PageUptodate(page)) &&
-                          (allocate_space || startio)) {
+                          (unmapped || startio)) {
+
                        if (!buffer_mapped(bh)) {
                                int     size;
-                               
+
                                /*
                                 * Getting here implies an unmapped buffer
                                 * was found, and we are in a path where we
@@ -454,13 +465,16 @@ delalloc_convert(
                                        if (startio) {
                                                bh_arr[cnt++] = bh;
                                        } else {
+                                               set_buffer_dirty(bh);
                                                unlock_buffer(bh);
                                        }
+                                       page_dirty = 0;
                                }
-                       } else if (startio && buffer_mapped(bh)) {
-                               if (buffer_uptodate(bh) && allocate_space) {
+                       } else if (startio) {
+                               if (buffer_uptodate(bh)) {
                                        lock_buffer(bh);
                                        bh_arr[cnt++] = bh;
+                                       page_dirty = 0;
                                }
                        }
                }
@@ -482,10 +496,10 @@ next_bh:
 
        if (mp) {
                cluster_write(inode, page->index + 1, mp,
-                               startio, allocate_space);
+                               startio, unmapped);
        }
 
-       return 0;
+       return page_dirty;
 
 error:
        for (i = 0; i < cnt; i++) {
@@ -494,12 +508,15 @@ error:
        
        /*
         * If it's delalloc and we have nowhere to put it,
-        * throw it away.
+        * throw it away, unless the lower layers told
+        * us to try again.
         */
-       if (!allocate_space) {
-               block_invalidatepage(page, 0);
+       if (err != -EAGAIN) {
+               if (!unmapped) {
+                       block_invalidatepage(page, 0);
+               }
+               ClearPageUptodate(page);
        }
-       ClearPageUptodate(page);
        return err;
 }
 
@@ -679,109 +696,172 @@ linvfs_readpages(
 }
 
 
-STATIC int
+STATIC void
 count_page_state(
        struct page             *page,
-       int                     *nr_delalloc,
-       int                     *nr_unmapped)
+       int                     *delalloc,
+       int                     *unmapped)
 {
-       *nr_delalloc = *nr_unmapped = 0;
-
-       if (page_has_buffers(page)) {
-               struct buffer_head      *bh, *head;
+       struct buffer_head      *bh, *head;
 
-               bh = head = page_buffers(page);
-               do {
-                       if (buffer_uptodate(bh) && !buffer_mapped(bh))
-                               (*nr_unmapped)++;
-                       else if (buffer_delay(bh))
-                               (*nr_delalloc)++;
-               } while ((bh = bh->b_this_page) != head);
+       *delalloc = *unmapped = 0;
 
-               return 1;
-       }
-
-       return 0;
+       bh = head = page_buffers(page);
+       do {
+               if (buffer_uptodate(bh) && !buffer_mapped(bh))
+                       (*unmapped) = 1;
+               else if (buffer_delay(bh))
+                       (*delalloc) = 1;
+       } while ((bh = bh->b_this_page) != head);
 }
 
+
+/*
+ * writepage: Called from one of two places:
+ *
+ * 1. we are flushing a delalloc buffer head.
+ *
+ * 2. we are writing out a dirty page. Typically the page dirty
+ *    state is cleared before we get here. In this case is it
+ *    conceivable we have no buffer heads.
+ *
+ * For delalloc space on the page we need to allocate space and
+ * flush it. For unmapped buffer heads on the page we should
+ * allocate space if the page is uptodate. For any other dirty
+ * buffer heads on the page we should flush them.
+ *
+ * If we detect that a transaction would be required to flush
+ * the page, we have to check the process flags first, if we
+ * are already in a transaction or disk I/O during allocations
+ * is off, we need to fail the writepage and redirty the page.
+ * We also need to set PF_NOIO ourselves.
+ */
 STATIC int
 linvfs_writepage(
        struct page             *page,
        struct writeback_control *wbc)
 {
        int                     error;
-       int                     need_trans = 1;
-       int                     nr_delalloc, nr_unmapped;
+       int                     need_trans;
+       int                     delalloc, unmapped;
+       struct inode            *inode = page->mapping->host;
 
-       if (count_page_state(page, &nr_delalloc, &nr_unmapped))
-               need_trans = nr_delalloc + nr_unmapped;
+       /*
+        * We need a transaction if:
+        *  1. There are delalloc buffers on the page
+        *  2. The page is upto date and we have unmapped buffers
+        *  3. The page is upto date and we have no buffers
+        */
+       if (!page_has_buffers(page)) {
+               unmapped = 1;
+               need_trans = 1;
+       } else {
+               count_page_state(page, &delalloc, &unmapped);
+               if (!PageUptodate(page))
+                       unmapped = 0;
+               need_trans = delalloc + unmapped;
+       }
 
+       /*
+        * If we need a transaction and the process flags say
+        * we are already in a transaction, or no IO is allowed
+        * then mark the page dirty again and leave the page
+        * as is.
+        */
        if ((current->flags & (PF_FSTRANS)) && need_trans)
                goto out_fail;
 
+       /*
+        * Delay hooking up buffer heads until we have
+        * made our go/no-go decision.
+        */
+       if (!page_has_buffers(page)) {
+               create_empty_buffers(page, 1 << inode->i_blkbits, 0);
+       }
+
        /*
         * Convert delalloc or unmapped space to real space and flush out
         * to disk.
         */
-       error = delalloc_convert(page, 1, nr_delalloc == 0);
-       if (unlikely(error))
-               unlock_page(page);
-       return error;
+       error = delalloc_convert(page, 1, unmapped);
+       if (error == -EAGAIN)
+               goto out_fail;
+       if (unlikely(error < 0))
+               goto out_unlock;
+
+       return 0;
 
 out_fail:
        set_page_dirty(page);
        unlock_page(page);
        return 0;
-}
-
-STATIC int
-linvfs_prepare_write(
-       struct file             *file,
-       struct page             *page,
-       unsigned int            from,
-       unsigned int            to)
-{
-       if (file && (file->f_flags & O_SYNC)) {
-               return block_prepare_write(page, from, to,
-                                               linvfs_get_block_sync);
-       } else {
-               return block_prepare_write(page, from, to,
-                                               linvfs_get_block);
-       }
+out_unlock:
+       unlock_page(page);
+       return error;
 }
 
 /*
- * This gets a page into cleanable state - page locked on entry
- * kept locked on exit. If the page is marked dirty we should
- * not come this way.
+ * Called to move a page into cleanable state - and from there
+ * to be released. Possibly the page is already clean. We always
+ * have buffer heads in this call.
+ *
+ * Returns 0 if the page is ok to release, 1 otherwise.
+ *
+ * Possible scenarios are:
+ *
+ * 1. We are being called to release a page which has been written
+ *    to via regular I/O. buffer heads will be dirty and possibly
+ *    delalloc. If no delalloc buffer heads in this case then we
+ *    can just return zero.
+ *
+ * 2. We are called to release a page which has been written via
+ *    mmap, all we need to do is ensure there is no delalloc
+ *    state in the buffer heads, if not we can let the caller
+ *    free them and we should come back later via writepage.
  */
 STATIC int
 linvfs_release_page(
        struct page             *page,
        int                     gfp_mask)
 {
-       int                     nr_delalloc, nr_unmapped;
+       int                     delalloc, unmapped;
 
-       if (count_page_state(page, &nr_delalloc, &nr_unmapped)) {
-               if (!nr_delalloc)
-                       goto free_buffers;
-       } 
+       count_page_state(page, &delalloc, &unmapped);
+       if (!delalloc)
+               goto free_buffers;
 
-       if (gfp_mask & __GFP_FS) {
-               /*
-                * Convert delalloc space to real space, do not flush the
-                * data out to disk, that will be done by the caller.
-                */
-               if (delalloc_convert(page, 0, 0) == 0)
-                       goto free_buffers;
-       }
+       if (!(gfp_mask & __GFP_FS))
+               return 0;
 
+       /*
+        * Convert delalloc space to real space, do not flush the
+        * data out to disk, that will be done by the caller.
+        * Never need to allocate space here - we will always
+        * come back to writepage in that case.
+        */
+       if (delalloc_convert(page, 0, 0) == 0)
+               goto free_buffers;
        return 0;
 
 free_buffers:
        return try_to_free_buffers(page);
 }
 
+STATIC int
+linvfs_prepare_write(
+       struct file             *file,
+       struct page             *page,
+       unsigned int            from,
+       unsigned int            to)
+{
+       if (file && (file->f_flags & O_SYNC)) {
+               return block_prepare_write(page, from, to,
+                                               linvfs_get_block_sync);
+       } else {
+               return block_prepare_write(page, from, to,
+                                               linvfs_get_block);
+       }
+}
 
 struct address_space_operations linvfs_aops = {
        .readpage               = linvfs_readpage,
index 322a8d38365543942c4ac3743668c5d53ea47126..9769d225fb726cc65eb060959e1a07ed8ba74bde 100644 (file)
@@ -120,7 +120,13 @@ xfs_iomap(
        case PBF_FILE_ALLOCATE:
                lockmode = XFS_ILOCK_SHARED|XFS_EXTSIZE_RD;
                bmap_flags = XFS_BMAPI_ENTIRE;
-               XFS_ILOCK(mp, io, lockmode);
+               /* Attempt non-blocking lock */
+               if (flags & PBF_TRYLOCK) {
+                       if (!XFS_ILOCK_NOWAIT(mp, io, lockmode))
+                               return XFS_ERROR(EAGAIN);
+               } else {
+                       XFS_ILOCK(mp, io, lockmode);
+               }
                break;
        case PBF_FILE_UNWRITTEN:
                lockmode = XFS_ILOCK_EXCL|XFS_EXTSIZE_WR;