static void cifs_fscache_inode_now_uncached(void *cookie_netfs_data) { struct cifsInodeInfo *cifsi = cookie_netfs_data; struct pagevec pvec; pgoff_t first; int loop, nr_pages; pagevec_init(&pvec, 0); first = 0; cFYI(1, "cifs inode 0x%p now uncached", cifsi); for (;;) { nr_pages = pagevec_lookup(&pvec, cifsi->vfs_inode.i_mapping, first, PAGEVEC_SIZE - pagevec_count(&pvec)); if (!nr_pages) break; for (loop = 0; loop < nr_pages; loop++) ClearPageFsCache(pvec.pages[loop]); first = pvec.pages[nr_pages - 1]->index + 1; pvec.nr = nr_pages; pagevec_release(&pvec); cond_resched(); } }
/** * nilfs_copy_back_pages -- copy back pages to original cache from shadow cache * @dmap: destination page cache * @smap: source page cache * * No pages must no be added to the cache during this process. * This must be ensured by the caller. */ void nilfs_copy_back_pages(struct address_space *dmap, struct address_space *smap) { struct pagevec pvec; unsigned int i, n; pgoff_t index = 0; int err; pagevec_init(&pvec); repeat: n = pagevec_lookup(&pvec, smap, &index); if (!n) return; for (i = 0; i < pagevec_count(&pvec); i++) { struct page *page = pvec.pages[i], *dpage; pgoff_t offset = page->index; lock_page(page); dpage = find_lock_page(dmap, offset); if (dpage) { /* override existing page on the destination cache */ WARN_ON(PageDirty(dpage)); nilfs_copy_page(dpage, page, 0); unlock_page(dpage); put_page(dpage); } else { struct page *page2; /* move the page to the destination cache */ spin_lock_irq(&smap->tree_lock); page2 = radix_tree_delete(&smap->page_tree, offset); WARN_ON(page2 != page); smap->nrpages--; spin_unlock_irq(&smap->tree_lock); spin_lock_irq(&dmap->tree_lock); err = radix_tree_insert(&dmap->page_tree, offset, page); if (unlikely(err < 0)) { WARN_ON(err == -EEXIST); page->mapping = NULL; put_page(page); /* for cache */ } else { page->mapping = dmap; dmap->nrpages++; if (PageDirty(page)) radix_tree_tag_set(&dmap->page_tree, offset, PAGECACHE_TAG_DIRTY); } spin_unlock_irq(&dmap->tree_lock); } unlock_page(page); } pagevec_release(&pvec); cond_resched(); goto repeat; }
/** * invalidate_inode_pages2 - remove all unmapped pages from an address_space * @mapping - the address_space * * invalidate_inode_pages2() is like truncate_inode_pages(), except for the case * where the page is seen to be mapped into process pagetables. In that case, * the page is marked clean but is left attached to its address_space. * * The page is also marked not uptodate so that a subsequent pagefault will * perform I/O to bringthe page's contents back into sync with its backing * store. * * FIXME: invalidate_inode_pages2() is probably trivially livelockable. */ void invalidate_inode_pages2(struct address_space *mapping) { struct pagevec pvec; pgoff_t next = 0; int i; pagevec_init(&pvec, 0); while (pagevec_lookup(&pvec, mapping, next, PAGEVEC_SIZE)) { for (i = 0; i < pagevec_count(&pvec); i++) { struct page *page = pvec.pages[i]; lock_page(page); if (page->mapping == mapping) { /* truncate race? */ wait_on_page_writeback(page); next = page->index + 1; if (page_mapped(page)) { clear_page_dirty(page); ClearPageUptodate(page); } else { if (!invalidate_complete_page(mapping, page)) { clear_page_dirty(page); ClearPageUptodate(page); } } } unlock_page(page); } pagevec_release(&pvec); cond_resched(); } }
/////////////////////////////////////////////////////////// // trace_pages // // /////////////////////////////////////////////////////////// unsigned trace_pages( IN struct address_space* mapping ) { struct pagevec pvec; pgoff_t next = 0; unsigned Ret = 0; int i; pagevec_init( &pvec, 0 ); while ( pagevec_lookup( &pvec, mapping, next, PAGEVEC_SIZE ) ) { for ( i = 0; i < pvec.nr; i++ ) { struct page *page = pvec.pages[i]; void* d = kmap_atomic( page, KM_USER0 ); DebugTrace( 0, UFSD_LEVEL_VFS, ("p=%p o=%llx f=%lx%s\n", page, (UINT64)page->index << PAGE_CACHE_SHIFT, page->flags, IsZero( d, PAGE_CACHE_SIZE )?", zero" : "" )); TracePageBuffers( page, 0 ); kunmap_atomic( d, KM_USER0 ); if ( page->index > next ) next = page->index; Ret += 1; next += 1; } pagevec_release(&pvec); } if ( 0 == next ) DebugTrace( 0, UFSD_LEVEL_VFS, ("no pages\n")); return Ret; }
/* * indication the cookie is no longer uncached * - this function is called when the backing store currently caching a cookie * is removed * - the netfs should use this to clean up any markers indicating cached pages * - this is mandatory for any object that may have data */ static void afs_vnode_cache_now_uncached(void *cookie_netfs_data) { struct afs_vnode *vnode = cookie_netfs_data; struct pagevec pvec; pgoff_t first; int loop, nr_pages; _enter("{%x,%x,%Lx}", vnode->fid.vnode, vnode->fid.unique, vnode->status.data_version); pagevec_init(&pvec, 0); first = 0; for (;;) { /* grab a bunch of pages to clean */ nr_pages = pagevec_lookup(&pvec, vnode->vfs_inode.i_mapping, first, PAGEVEC_SIZE - pagevec_count(&pvec)); if (!nr_pages) break; for (loop = 0; loop < nr_pages; loop++) ClearPageFsCache(pvec.pages[loop]); first = pvec.pages[nr_pages - 1]->index + 1; pvec.nr = nr_pages; pagevec_release(&pvec); cond_resched(); } _leave(""); }
/* * Indication from FS-Cache that the cookie is no longer cached * - This function is called when the backing store currently caching a cookie * is removed * - The netfs should use this to clean up any markers indicating cached pages * - This is mandatory for any object that may have data */ static void nfs_fscache_inode_now_uncached(void *cookie_netfs_data) { struct nfs_inode *nfsi = cookie_netfs_data; struct pagevec pvec; pgoff_t first; int loop, nr_pages; pagevec_init(&pvec, 0); first = 0; dprintk("NFS: nfs_inode_now_uncached: nfs_inode 0x%p\n", nfsi); for (;;) { /* grab a bunch of pages to unmark */ nr_pages = pagevec_lookup(&pvec, nfsi->vfs_inode.i_mapping, first, PAGEVEC_SIZE - pagevec_count(&pvec)); if (!nr_pages) break; for (loop = 0; loop < nr_pages; loop++) ClearPageFsCache(pvec.pages[loop]); first = pvec.pages[nr_pages - 1]->index + 1; pvec.nr = nr_pages; pagevec_release(&pvec); cond_resched(); } }
static void v9fs_cache_inode_now_uncached(void *cookie_netfs_data) { struct v9fs_inode *v9inode = cookie_netfs_data; struct pagevec pvec; pgoff_t first; int loop, nr_pages; pagevec_init(&pvec, 0); first = 0; for (;;) { nr_pages = pagevec_lookup(&pvec, v9inode->vfs_inode.i_mapping, first, PAGEVEC_SIZE - pagevec_count(&pvec)); if (!nr_pages) break; for (loop = 0; loop < nr_pages; loop++) ClearPageFsCache(pvec.pages[loop]); first = pvec.pages[nr_pages - 1]->index + 1; pvec.nr = nr_pages; pagevec_release(&pvec); cond_resched(); } }
/** * invalidate_mapping_pages - Invalidate all the unlocked pages of one inode * @mapping: the address_space which holds the pages to invalidate * @start: the offset 'from' which to invalidate * @end: the offset 'to' which to invalidate (inclusive) * * This function only removes the unlocked pages, if you want to * remove all the pages of one inode, you must call truncate_inode_pages. * * invalidate_mapping_pages() will not block on IO activity. It will not * invalidate pages which are dirty, locked, under writeback or mapped into * pagetables. */ unsigned long invalidate_mapping_pages(struct address_space *mapping, pgoff_t start, pgoff_t end) { struct pagevec pvec; pgoff_t index = start; unsigned long ret; unsigned long count = 0; int i; /* * Note: this function may get called on a shmem/tmpfs mapping: * pagevec_lookup() might then return 0 prematurely (because it * got a gangful of swap entries); but it's hardly worth worrying * about - it can rarely have anything to free from such a mapping * (most pages are dirty), and already skips over any difficulties. */ pagevec_init(&pvec, 0); while (index <= end && pagevec_lookup(&pvec, mapping, index, min(end - index, (pgoff_t)PAGEVEC_SIZE - 1) + 1)) { mem_cgroup_uncharge_start(); for (i = 0; i < pagevec_count(&pvec); i++) { struct page *page = pvec.pages[i]; /* We rely upon deletion not changing page->index */ index = page->index; if (index > end) break; if (!trylock_page(page)) continue; WARN_ON(page->index != index); ret = invalidate_inode_page(page); unlock_page(page); /* * Invalidation is a hint that the page is no longer * of interest and try to speed up its reclaim. */ if (!ret) deactivate_page(page); count += ret; } pagevec_release(&pvec); mem_cgroup_uncharge_end(); cond_resched(); index++; } return count; }
/* * Radix-tree checker */ void nilfs_check_radix_tree(const char *fname, int line, struct address_space *mapping, int tag) { struct pagevec pvec; unsigned int i, n; pgoff_t index = 0; char *page_type; int nr_found = 0; if (tag == PAGECACHE_TAG_DIRTY) page_type = "dirty"; else if (tag == PAGECACHE_TAG_WRITEBACK) page_type = "writeback"; else page_type = "leaking"; pagevec_init(&pvec, 0); repeat: if (tag < 0) { n = pagevec_lookup(&pvec, mapping, index, PAGEVEC_SIZE); if (n) index = pvec.pages[n - 1]->index + 1; } else n = pagevec_lookup_tag(&pvec, mapping, &index, tag, PAGEVEC_SIZE); if (!n) { if (nr_found) printk(KERN_WARNING "%s: found %d %s pages\n", fname, nr_found, page_type); return; } for (i = 0; i < n; i++) { nilfs_page_debug(fname, line, pvec.pages[i], "%s page", page_type); nr_found++; } pagevec_release(&pvec); cond_resched(); goto repeat; }
/** * invalidate_mapping_pages - Invalidate all the unlocked pages of one inode * @mapping: the address_space which holds the pages to invalidate * @start: the offset 'from' which to invalidate * @end: the offset 'to' which to invalidate (inclusive) * * This function only removes the unlocked pages, if you want to * remove all the pages of one inode, you must call truncate_inode_pages. * * invalidate_mapping_pages() will not block on IO activity. It will not * invalidate pages which are dirty, locked, under writeback or mapped into * pagetables. */ unsigned long invalidate_mapping_pages(struct address_space *mapping, pgoff_t start, pgoff_t end) { struct pagevec pvec; pgoff_t next = start; unsigned long ret = 0; int i; pagevec_init(&pvec, 0); while (next <= end && pagevec_lookup(&pvec, mapping, next, PAGEVEC_SIZE)) { for (i = 0; i < pagevec_count(&pvec); i++) { struct page *page = pvec.pages[i]; if (TestSetPageLocked(page)) { next++; continue; } if (page->index > next) next = page->index; next++; if (PageDirty(page) || PageWriteback(page)) goto unlock; if (page_mapped(page)) goto unlock; ret += invalidate_complete_page(mapping, page); unlock: unlock_page(page); if (next > end) break; } pagevec_release(&pvec); cond_resched(); } return ret; }
/** * truncate_inode_pages - truncate *all* the pages from an offset * @mapping: mapping to truncate * @lstart: offset from which to truncate * * Truncate the page cache at a set offset, removing the pages that are beyond * that offset (and zeroing out partial pages). * * Truncate takes two passes - the first pass is nonblocking. It will not * block on page locks and it will not block on writeback. The second pass * will wait. This is to prevent as much IO as possible in the affected region. * The first pass will remove most pages, so the search cost of the second pass * is low. * * When looking at page->index outside the page lock we need to be careful to * copy it into a local to avoid races (it could change at any time). * * We pass down the cache-hot hint to the page freeing code. Even if the * mapping is large, it is probably the case that the final pages are the most * recently touched, and freeing happens in ascending file offset order. * * Called under (and serialised by) inode->i_sem. */ void truncate_inode_pages(struct address_space *mapping, loff_t lstart) { const pgoff_t start = (lstart + PAGE_CACHE_SIZE-1) >> PAGE_CACHE_SHIFT; const unsigned partial = lstart & (PAGE_CACHE_SIZE - 1); struct pagevec pvec; pgoff_t next; int i; if (mapping->nrpages == 0) return; pagevec_init(&pvec, 0); next = start; while (pagevec_lookup(&pvec, mapping, next, PAGEVEC_SIZE)) { for (i = 0; i < pagevec_count(&pvec); i++) { struct page *page = pvec.pages[i]; pgoff_t page_index = page->index; if (page_index > next) next = page_index; next++; if (TestSetPageLocked(page)) continue; if (PageWriteback(page)) { unlock_page(page); continue; } truncate_complete_page(mapping, page); unlock_page(page); } pagevec_release(&pvec); cond_resched(); } if (partial) { struct page *page = find_lock_page(mapping, start - 1); if (page) { wait_on_page_writeback(page); truncate_partial_page(page, partial); unlock_page(page); page_cache_release(page); } } next = start; for ( ; ; ) { cond_resched(); if (!pagevec_lookup(&pvec, mapping, next, PAGEVEC_SIZE)) { if (next == start) break; next = start; continue; } for (i = 0; i < pagevec_count(&pvec); i++) { struct page *page = pvec.pages[i]; lock_page(page); wait_on_page_writeback(page); if (page->index > next) next = page->index; next++; truncate_complete_page(mapping, page); unlock_page(page); } pagevec_release(&pvec); } }
/** * invalidate_inode_pages2_range - remove range of pages from an address_space * @mapping: the address_space * @start: the page offset 'from' which to invalidate * @end: the page offset 'to' which to invalidate (inclusive) * * Any pages which are found to be mapped into pagetables are unmapped prior to * invalidation. * * Returns -EBUSY if any pages could not be invalidated. */ int invalidate_inode_pages2_range(struct address_space *mapping, pgoff_t start, pgoff_t end) { struct pagevec pvec; pgoff_t index; int i; int ret = 0; int ret2 = 0; int did_range_unmap = 0; cleancache_invalidate_inode(mapping); pagevec_init(&pvec, 0); index = start; while (index <= end && pagevec_lookup(&pvec, mapping, index, min(end - index, (pgoff_t)PAGEVEC_SIZE - 1) + 1)) { mem_cgroup_uncharge_start(); for (i = 0; i < pagevec_count(&pvec); i++) { struct page *page = pvec.pages[i]; /* We rely upon deletion not changing page->index */ index = page->index; if (index > end) break; lock_page(page); WARN_ON(page->index != index); if (page->mapping != mapping) { unlock_page(page); continue; } wait_on_page_writeback(page); if (page_mapped(page)) { if (!did_range_unmap) { /* * Zap the rest of the file in one hit. */ unmap_mapping_range(mapping, (loff_t)index << PAGE_CACHE_SHIFT, (loff_t)(1 + end - index) << PAGE_CACHE_SHIFT, 0); did_range_unmap = 1; } else { /* * Just zap this page */ unmap_mapping_range(mapping, (loff_t)index << PAGE_CACHE_SHIFT, PAGE_CACHE_SIZE, 0); } } BUG_ON(page_mapped(page)); ret2 = do_launder_page(mapping, page); if (ret2 == 0) { if (!invalidate_complete_page2(mapping, page)) ret2 = -EBUSY; } if (ret2 < 0) ret = ret2; unlock_page(page); } pagevec_release(&pvec); mem_cgroup_uncharge_end(); cond_resched(); index++; } cleancache_invalidate_inode(mapping); return ret; }
/** * truncate_inode_pages_range - truncate range of pages specified by start & end byte offsets * @mapping: mapping to truncate * @lstart: offset from which to truncate * @lend: offset to which to truncate (inclusive) * * Truncate the page cache, removing the pages that are between * specified offsets (and zeroing out partial pages * if lstart or lend + 1 is not page aligned). * * Truncate takes two passes - the first pass is nonblocking. It will not * block on page locks and it will not block on writeback. The second pass * will wait. This is to prevent as much IO as possible in the affected region. * The first pass will remove most pages, so the search cost of the second pass * is low. * * We pass down the cache-hot hint to the page freeing code. Even if the * mapping is large, it is probably the case that the final pages are the most * recently touched, and freeing happens in ascending file offset order. * * Note that since ->invalidatepage() accepts range to invalidate * truncate_inode_pages_range is able to handle cases where lend + 1 is not * page aligned properly. */ void truncate_inode_pages_range(struct address_space *mapping, loff_t lstart, loff_t lend) { pgoff_t start; /* inclusive */ pgoff_t end; /* exclusive */ unsigned int partial_start; /* inclusive */ unsigned int partial_end; /* exclusive */ struct pagevec pvec; pgoff_t index; int i; cleancache_invalidate_inode(mapping); if (mapping->nrpages == 0) return; /* Offsets within partial pages */ partial_start = lstart & (PAGE_CACHE_SIZE - 1); partial_end = (lend + 1) & (PAGE_CACHE_SIZE - 1); /* * 'start' and 'end' always covers the range of pages to be fully * truncated. Partial pages are covered with 'partial_start' at the * start of the range and 'partial_end' at the end of the range. * Note that 'end' is exclusive while 'lend' is inclusive. */ start = (lstart + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; if (lend == -1) /* * lend == -1 indicates end-of-file so we have to set 'end' * to the highest possible pgoff_t and since the type is * unsigned we're using -1. */ end = -1; else end = (lend + 1) >> PAGE_CACHE_SHIFT; pagevec_init(&pvec, 0); index = start; while (index < end && pagevec_lookup(&pvec, mapping, index, min(end - index, (pgoff_t)PAGEVEC_SIZE))) { mem_cgroup_uncharge_start(); for (i = 0; i < pagevec_count(&pvec); i++) { struct page *page = pvec.pages[i]; /* We rely upon deletion not changing page->index */ index = page->index; if (index >= end) break; if (!trylock_page(page)) continue; WARN_ON(page->index != index); if (PageWriteback(page)) { unlock_page(page); continue; } truncate_inode_page(mapping, page); unlock_page(page); } pagevec_release(&pvec); mem_cgroup_uncharge_end(); cond_resched(); index++; } if (partial_start) { struct page *page = find_lock_page(mapping, start - 1); if (page) { unsigned int top = PAGE_CACHE_SIZE; if (start > end) { /* Truncation within a single page */ top = partial_end; partial_end = 0; } wait_on_page_writeback(page); zero_user_segment(page, partial_start, top); cleancache_invalidate_page(mapping, page); if (page_has_private(page)) do_invalidatepage(page, partial_start, top - partial_start); unlock_page(page); page_cache_release(page); } } if (partial_end) { struct page *page = find_lock_page(mapping, end); if (page) { wait_on_page_writeback(page); zero_user_segment(page, 0, partial_end); cleancache_invalidate_page(mapping, page); if (page_has_private(page)) do_invalidatepage(page, 0, partial_end); unlock_page(page); page_cache_release(page); } } /* * If the truncation happened within a single page no pages * will be released, just zeroed, so we can bail out now. */ if (start >= end) return; index = start; for ( ; ; ) { cond_resched(); if (!pagevec_lookup(&pvec, mapping, index, min(end - index, (pgoff_t)PAGEVEC_SIZE))) { if (index == start) break; index = start; continue; } if (index == start && pvec.pages[0]->index >= end) { pagevec_release(&pvec); break; } mem_cgroup_uncharge_start(); for (i = 0; i < pagevec_count(&pvec); i++) { struct page *page = pvec.pages[i]; /* We rely upon deletion not changing page->index */ index = page->index; if (index >= end) break; lock_page(page); WARN_ON(page->index != index); wait_on_page_writeback(page); truncate_inode_page(mapping, page); unlock_page(page); } pagevec_release(&pvec); mem_cgroup_uncharge_end(); index++; } cleancache_invalidate_inode(mapping); }
/* * This routine is called to find out and return a data or hole offset * from the page cache for unwritten extents according to the desired * type for xfs_seek_data() or xfs_seek_hole(). * * The argument offset is used to tell where we start to search from the * page cache. Map is used to figure out the end points of the range to * lookup pages. * * Return true if the desired type of offset was found, and the argument * offset is filled with that address. Otherwise, return false and keep * offset unchanged. */ STATIC bool xfs_find_get_desired_pgoff( struct inode *inode, struct xfs_bmbt_irec *map, unsigned int type, loff_t *offset) { struct xfs_inode *ip = XFS_I(inode); struct xfs_mount *mp = ip->i_mount; struct pagevec pvec; pgoff_t index; pgoff_t end; loff_t endoff; loff_t startoff = *offset; loff_t lastoff = startoff; bool found = false; pagevec_init(&pvec, 0); index = startoff >> PAGE_CACHE_SHIFT; endoff = XFS_FSB_TO_B(mp, map->br_startoff + map->br_blockcount); end = endoff >> PAGE_CACHE_SHIFT; do { int want; unsigned nr_pages; unsigned int i; want = min_t(pgoff_t, end - index, PAGEVEC_SIZE); nr_pages = pagevec_lookup(&pvec, inode->i_mapping, index, want); /* * No page mapped into given range. If we are searching holes * and if this is the first time we got into the loop, it means * that the given offset is landed in a hole, return it. * * If we have already stepped through some block buffers to find * holes but they all contains data. In this case, the last * offset is already updated and pointed to the end of the last * mapped page, if it does not reach the endpoint to search, * that means there should be a hole between them. */ if (nr_pages == 0) { /* Data search found nothing */ if (type == DATA_OFF) break; ASSERT(type == HOLE_OFF); if (lastoff == startoff || lastoff < endoff) { found = true; *offset = lastoff; } break; } /* * At lease we found one page. If this is the first time we * step into the loop, and if the first page index offset is * greater than the given search offset, a hole was found. */ if (type == HOLE_OFF && lastoff == startoff && lastoff < page_offset(pvec.pages[0])) { found = true; break; } for (i = 0; i < nr_pages; i++) { struct page *page = pvec.pages[i]; loff_t b_offset; /* * 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. * * Searching done if the page index is out of range. * If the current offset is not reaches the end of * the specified search range, there should be a hole * between them. */ if (page->index > end) { if (type == HOLE_OFF && lastoff < endoff) { *offset = lastoff; found = true; } goto out; } lock_page(page); /* * Page truncated or invalidated(page->mapping == NULL). * We can freely skip it and proceed to check the next * page. */ if (unlikely(page->mapping != inode->i_mapping)) { unlock_page(page); continue; } if (!page_has_buffers(page)) { unlock_page(page); continue; } found = xfs_lookup_buffer_offset(page, &b_offset, type); if (found) { /* * The found offset may be less than the start * point to search if this is the first time to * come here. */ *offset = max_t(loff_t, startoff, b_offset); unlock_page(page); goto out; } /* * We either searching data but nothing was found, or * searching hole but found a data buffer. In either * case, probably the next page contains the desired * things, update the last offset to it so. */ lastoff = page_offset(page) + PAGE_SIZE; unlock_page(page); } /* * The number of returned pages less than our desired, search * done. In this case, nothing was found for searching data, * but we found a hole behind the last offset. */ if (nr_pages < want) { if (type == HOLE_OFF) { *offset = lastoff; found = true; } break; } index = pvec.pages[i - 1]->index + 1; pagevec_release(&pvec); } while (index <= end); out: pagevec_release(&pvec); return found; }