}
 EXPORT_SYMBOL(bh_submit_read);
 
+/*
+ * Seek for SEEK_DATA / SEEK_HOLE within @page, starting at @lastoff.
+ *
+ * Returns the offset within the file on success, and -ENOENT otherwise.
+ */
+static loff_t
+page_seek_hole_data(struct page *page, loff_t lastoff, int whence)
+{
+       loff_t offset = page_offset(page);
+       struct buffer_head *bh, *head;
+       bool seek_data = whence == SEEK_DATA;
+
+       if (lastoff < offset)
+               lastoff = offset;
+
+       bh = head = page_buffers(page);
+       do {
+               offset += bh->b_size;
+               if (lastoff >= offset)
+                       continue;
+
+               /*
+                * Unwritten extents that have data in the page cache covering
+                * them can be identified by the BH_Unwritten state flag.
+                * Pages with multiple buffers might have a mix of holes, data
+                * and unwritten extents - any buffer with valid data in it
+                * should have BH_Uptodate flag set on it.
+                */
+
+               if ((buffer_unwritten(bh) || buffer_uptodate(bh)) == seek_data)
+                       return lastoff;
+
+               lastoff = offset;
+       } while ((bh = bh->b_this_page) != head);
+       return -ENOENT;
+}
+
+/*
+ * Seek for SEEK_DATA / SEEK_HOLE in the page cache.
+ *
+ * Within unwritten extents, the page cache determines which parts are holes
+ * and which are data: unwritten and uptodate buffer heads count as data;
+ * everything else counts as a hole.
+ *
+ * Returns the resulting offset on successs, and -ENOENT otherwise.
+ */
+loff_t
+page_cache_seek_hole_data(struct inode *inode, loff_t offset, loff_t length,
+                         int whence)
+{
+       pgoff_t index = offset >> PAGE_SHIFT;
+       pgoff_t end = DIV_ROUND_UP(offset + length, PAGE_SIZE);
+       loff_t lastoff = offset;
+       struct pagevec pvec;
+
+       if (length <= 0)
+               return -ENOENT;
+
+       pagevec_init(&pvec, 0);
+
+       do {
+               unsigned want, nr_pages, i;
+
+               want = min_t(unsigned, end - index, PAGEVEC_SIZE);
+               nr_pages = pagevec_lookup(&pvec, inode->i_mapping, index, want);
+               if (nr_pages == 0)
+                       break;
+
+               for (i = 0; i < nr_pages; i++) {
+                       struct page *page = pvec.pages[i];
+
+                       /*
+                        * At this point, the page may be truncated or
+                        * invalidated (changing page->mapping to NULL), or
+                        * even swizzled back from swapper_space to tmpfs file
+                        * mapping.  However, page->index will not change
+                        * because we have a reference on the page.
+                         *
+                        * If current page offset is beyond where we've ended,
+                        * we've found a hole.
+                         */
+                       if (whence == SEEK_HOLE &&
+                           lastoff < page_offset(page))
+                               goto check_range;
+
+                       /* Searching done if the page index is out of range. */
+                       if (page->index >= end)
+                               goto not_found;
+
+                       lock_page(page);
+                       if (likely(page->mapping == inode->i_mapping) &&
+                           page_has_buffers(page)) {
+                               lastoff = page_seek_hole_data(page, lastoff, whence);
+                               if (lastoff >= 0) {
+                                       unlock_page(page);
+                                       goto check_range;
+                               }
+                       }
+                       unlock_page(page);
+                       lastoff = page_offset(page) + PAGE_SIZE;
+               }
+
+               /* Searching done if fewer pages returned than wanted. */
+               if (nr_pages < want)
+                       break;
+
+               index = pvec.pages[i - 1]->index + 1;
+               pagevec_release(&pvec);
+       } while (index < end);
+
+       /* When no page at lastoff and we are not done, we found a hole. */
+       if (whence != SEEK_HOLE)
+               goto not_found;
+
+check_range:
+       if (lastoff < offset + length)
+               goto out;
+not_found:
+       lastoff = -ENOENT;
+out:
+       pagevec_release(&pvec);
+       return lastoff;
+}
+
 void __init buffer_init(void)
 {
        unsigned long nrpages;