ipc: move locking out of ipcctl_pre_down_nolock
[cascardo/linux.git] / fs / buffer.c
index d2a4d1b..4d74335 100644 (file)
@@ -82,6 +82,40 @@ void unlock_buffer(struct buffer_head *bh)
 }
 EXPORT_SYMBOL(unlock_buffer);
 
+/*
+ * Returns if the page has dirty or writeback buffers. If all the buffers
+ * are unlocked and clean then the PageDirty information is stale. If
+ * any of the pages are locked, it is assumed they are locked for IO.
+ */
+void buffer_check_dirty_writeback(struct page *page,
+                                    bool *dirty, bool *writeback)
+{
+       struct buffer_head *head, *bh;
+       *dirty = false;
+       *writeback = false;
+
+       BUG_ON(!PageLocked(page));
+
+       if (!page_has_buffers(page))
+               return;
+
+       if (PageWriteback(page))
+               *writeback = true;
+
+       head = page_buffers(page);
+       bh = head;
+       do {
+               if (buffer_locked(bh))
+                       *writeback = true;
+
+               if (buffer_dirty(bh))
+                       *dirty = true;
+
+               bh = bh->b_this_page;
+       } while (bh != head);
+}
+EXPORT_SYMBOL(buffer_check_dirty_writeback);
+
 /*
  * Block until a buffer comes unlocked.  This doesn't stop it
  * from becoming locked again - you have to lock it yourself
@@ -1454,7 +1488,8 @@ static void discard_buffer(struct buffer_head * bh)
  * block_invalidatepage - invalidate part or all of a buffer-backed page
  *
  * @page: the page which is affected
- * @offset: the index of the truncation point
+ * @offset: start of the range to invalidate
+ * @length: length of the range to invalidate
  *
  * block_invalidatepage() is called when all or part of the page has become
  * invalidated by a truncate operation.
@@ -1465,21 +1500,34 @@ static void discard_buffer(struct buffer_head * bh)
  * point.  Because the caller is about to free (and possibly reuse) those
  * blocks on-disk.
  */
-void block_invalidatepage(struct page *page, unsigned long offset)
+void block_invalidatepage(struct page *page, unsigned int offset,
+                         unsigned int length)
 {
        struct buffer_head *head, *bh, *next;
        unsigned int curr_off = 0;
+       unsigned int stop = length + offset;
 
        BUG_ON(!PageLocked(page));
        if (!page_has_buffers(page))
                goto out;
 
+       /*
+        * Check for overflow
+        */
+       BUG_ON(stop > PAGE_CACHE_SIZE || stop < length);
+
        head = page_buffers(page);
        bh = head;
        do {
                unsigned int next_off = curr_off + bh->b_size;
                next = bh->b_this_page;
 
+               /*
+                * Are we still fully in range ?
+                */
+               if (next_off > stop)
+                       goto out;
+
                /*
                 * is this block fully invalidated?
                 */
@@ -1501,6 +1549,7 @@ out:
 }
 EXPORT_SYMBOL(block_invalidatepage);
 
+
 /*
  * We attach and possibly dirty the buffers atomically wrt
  * __set_page_dirty_buffers() via private_lock.  try_to_free_buffers
@@ -2841,7 +2890,7 @@ int block_write_full_page_endio(struct page *page, get_block_t *get_block,
                 * they may have been added in ext3_writepage().  Make them
                 * freeable here, so the page does not leak.
                 */
-               do_invalidatepage(page, 0);
+               do_invalidatepage(page, 0, PAGE_CACHE_SIZE);
                unlock_page(page);
                return 0; /* don't care */
        }