Merge to Fedora kernel-2.6.18-1.2224_FC5 patched with stable patch-2.6.18.1-vs2.0...
[linux-2.6.git] / fs / splice.c
index 8fef667..4eed2f6 100644 (file)
@@ -55,31 +55,43 @@ static int page_cache_pipe_buf_steal(struct pipe_inode_info *pipe,
                                     struct pipe_buffer *buf)
 {
        struct page *page = buf->page;
-       struct address_space *mapping = page_mapping(page);
+       struct address_space *mapping;
 
        lock_page(page);
 
-       WARN_ON(!PageUptodate(page));
+       mapping = page_mapping(page);
+       if (mapping) {
+               WARN_ON(!PageUptodate(page));
 
-       /*
-        * At least for ext2 with nobh option, we need to wait on writeback
-        * completing on this page, since we'll remove it from the pagecache.
-        * Otherwise truncate wont wait on the page, allowing the disk
-        * blocks to be reused by someone else before we actually wrote our
-        * data to them. fs corruption ensues.
-        */
-       wait_on_page_writeback(page);
+               /*
+                * At least for ext2 with nobh option, we need to wait on
+                * writeback completing on this page, since we'll remove it
+                * from the pagecache.  Otherwise truncate wont wait on the
+                * page, allowing the disk blocks to be reused by someone else
+                * before we actually wrote our data to them. fs corruption
+                * ensues.
+                */
+               wait_on_page_writeback(page);
 
-       if (PagePrivate(page))
-               try_to_release_page(page, mapping_gfp_mask(mapping));
+               if (PagePrivate(page))
+                       try_to_release_page(page, mapping_gfp_mask(mapping));
 
-       if (!remove_mapping(mapping, page)) {
-               unlock_page(page);
-               return 1;
+               /*
+                * If we succeeded in removing the mapping, set LRU flag
+                * and return good.
+                */
+               if (remove_mapping(mapping, page)) {
+                       buf->flags |= PIPE_BUF_FLAG_LRU;
+                       return 0;
+               }
        }
 
-       buf->flags |= PIPE_BUF_FLAG_LRU;
-       return 0;
+       /*
+        * Raced with truncate or failed to remove page from current
+        * address space, unlock and return failure.
+        */
+       unlock_page(page);
+       return 1;
 }
 
 static void page_cache_pipe_buf_release(struct pipe_inode_info *pipe,
@@ -595,7 +607,7 @@ find_page:
                        ret = -ENOMEM;
                        page = page_cache_alloc_cold(mapping);
                        if (unlikely(!page))
-                               goto out_nomem;
+                               goto out_ret;
 
                        /*
                         * This will also lock the page
@@ -654,7 +666,7 @@ find_page:
                if (sd->pos + this_len > isize)
                        vmtruncate(mapping->host, isize);
 
-               goto out;
+               goto out_ret;
        }
 
        if (buf->page != page) {
@@ -686,7 +698,7 @@ find_page:
 out:
        page_cache_release(page);
        unlock_page(page);
-out_nomem:
+out_ret:
        return ret;
 }
 
@@ -1389,11 +1401,11 @@ static int link_pipe(struct pipe_inode_info *ipipe,
         * could deadlock (one doing tee from A -> B, the other from B -> A).
         */
        if (ipipe->inode < opipe->inode) {
-               mutex_lock(&ipipe->inode->i_mutex);
-               mutex_lock(&opipe->inode->i_mutex);
+               mutex_lock_nested(&ipipe->inode->i_mutex, I_MUTEX_PARENT);
+               mutex_lock_nested(&opipe->inode->i_mutex, I_MUTEX_CHILD);
        } else {
-               mutex_lock(&opipe->inode->i_mutex);
-               mutex_lock(&ipipe->inode->i_mutex);
+               mutex_lock_nested(&opipe->inode->i_mutex, I_MUTEX_PARENT);
+               mutex_lock_nested(&ipipe->inode->i_mutex, I_MUTEX_CHILD);
        }
 
        do {