readahead: remove sync/async readahead call dependency
[sfrench/cifs-2.6.git] / mm / readahead.c
index 9ce303d4b8109a32bf198ffc02ba4fe6b77eec49..d7c6e143a1297fc4866b18b28f04be2f9aaf2ecb 100644 (file)
@@ -31,6 +31,42 @@ EXPORT_SYMBOL_GPL(file_ra_state_init);
 
 #define list_to_page(head) (list_entry((head)->prev, struct page, lru))
 
+/*
+ * see if a page needs releasing upon read_cache_pages() failure
+ * - the caller of read_cache_pages() may have set PG_private or PG_fscache
+ *   before calling, such as the NFS fs marking pages that are cached locally
+ *   on disk, thus we need to give the fs a chance to clean up in the event of
+ *   an error
+ */
+static void read_cache_pages_invalidate_page(struct address_space *mapping,
+                                            struct page *page)
+{
+       if (page_has_private(page)) {
+               if (!trylock_page(page))
+                       BUG();
+               page->mapping = mapping;
+               do_invalidatepage(page, 0);
+               page->mapping = NULL;
+               unlock_page(page);
+       }
+       page_cache_release(page);
+}
+
+/*
+ * release a list of pages, invalidating them first if need be
+ */
+static void read_cache_pages_invalidate_pages(struct address_space *mapping,
+                                             struct list_head *pages)
+{
+       struct page *victim;
+
+       while (!list_empty(pages)) {
+               victim = list_to_page(pages);
+               list_del(&victim->lru);
+               read_cache_pages_invalidate_page(mapping, victim);
+       }
+}
+
 /**
  * read_cache_pages - populate an address space with some pages & start reads against them
  * @mapping: the address_space
@@ -52,14 +88,14 @@ int read_cache_pages(struct address_space *mapping, struct list_head *pages,
                list_del(&page->lru);
                if (add_to_page_cache_lru(page, mapping,
                                        page->index, GFP_KERNEL)) {
-                       page_cache_release(page);
+                       read_cache_pages_invalidate_page(mapping, page);
                        continue;
                }
                page_cache_release(page);
 
                ret = filler(data, page);
                if (unlikely(ret)) {
-                       put_pages_list(pages);
+                       read_cache_pages_invalidate_pages(mapping, pages);
                        break;
                }
                task_io_account_read(PAGE_CACHE_SIZE);
@@ -174,6 +210,7 @@ int force_page_cache_readahead(struct address_space *mapping, struct file *filp,
        if (unlikely(!mapping->a_ops->readpage && !mapping->a_ops->readpages))
                return -EINVAL;
 
+       nr_to_read = max_sane_readahead(nr_to_read);
        while (nr_to_read) {
                int err;
 
@@ -320,7 +357,7 @@ ondemand_readahead(struct address_space *mapping,
                   bool hit_readahead_marker, pgoff_t offset,
                   unsigned long req_size)
 {
-       int     max = ra->ra_pages;     /* max readahead pages */
+       unsigned long max = max_sane_readahead(ra->ra_pages);
        pgoff_t prev_offset;
        int     sequential;
 
@@ -358,7 +395,7 @@ ondemand_readahead(struct address_space *mapping,
                pgoff_t start;
 
                rcu_read_lock();
-               start = radix_tree_next_hole(&mapping->page_tree, offset,max+1);
+               start = radix_tree_next_hole(&mapping->page_tree, offset+1,max);
                rcu_read_unlock();
 
                if (!start || start - offset > max)
@@ -366,6 +403,7 @@ ondemand_readahead(struct address_space *mapping,
 
                ra->start = start;
                ra->size = start - offset;      /* old async_size */
+               ra->size += req_size;
                ra->size = get_next_ra_size(ra, max);
                ra->async_size = ra->size;
                goto readit;
@@ -383,6 +421,16 @@ ondemand_readahead(struct address_space *mapping,
        ra->async_size = ra->size > req_size ? ra->size - req_size : ra->size;
 
 readit:
+       /*
+        * Will this read hit the readahead marker made by itself?
+        * If so, trigger the readahead marker hit now, and merge
+        * the resulted next readahead window into the current one.
+        */
+       if (offset == ra->start && ra->size == ra->async_size) {
+               ra->async_size = get_next_ra_size(ra, max);
+               ra->size += ra->async_size;
+       }
+
        return ra_submit(ra, mapping, filp);
 }