Skip to content
Snippets Groups Projects
vmscan.c 84.7 KiB
Newer Older
  • Learn to ignore specific revisions
  •  * Scan @zone's unevictable LRU lists to check for pages that have become
     * evictable.  Move those that have to @zone's inactive list where they
     * become candidates for reclaim, unless shrink_inactive_zone() decides
     * to reactivate them.  Pages that are still unevictable are rotated
     * back onto @zone's unevictable list.
     */
    #define SCAN_UNEVICTABLE_BATCH_SIZE 16UL /* arbitrary lock hold batch size */
    
    static void scan_zone_unevictable_pages(struct zone *zone)
    
    {
    	struct list_head *l_unevictable = &zone->lru[LRU_UNEVICTABLE].list;
    	unsigned long scan;
    	unsigned long nr_to_scan = zone_page_state(zone, NR_UNEVICTABLE);
    
    	while (nr_to_scan > 0) {
    		unsigned long batch_size = min(nr_to_scan,
    						SCAN_UNEVICTABLE_BATCH_SIZE);
    
    		spin_lock_irq(&zone->lru_lock);
    		for (scan = 0;  scan < batch_size; scan++) {
    			struct page *page = lru_to_page(l_unevictable);
    
    			if (!trylock_page(page))
    				continue;
    
    			prefetchw_prev_lru_page(page, l_unevictable, flags);
    
    			if (likely(PageLRU(page) && PageUnevictable(page)))
    				check_move_unevictable_page(page, zone);
    
    			unlock_page(page);
    		}
    		spin_unlock_irq(&zone->lru_lock);
    
    		nr_to_scan -= batch_size;
    	}
    }
    
    
    /**
     * scan_all_zones_unevictable_pages - scan all unevictable lists for evictable pages
     *
     * A really big hammer:  scan all zones' unevictable LRU lists to check for
     * pages that have become evictable.  Move those back to the zones'
     * inactive list where they become candidates for reclaim.
     * This occurs when, e.g., we have unswappable pages on the unevictable lists,
     * and we add swap to the system.  As such, it runs in the context of a task
     * that has possibly/probably made some previously unevictable pages
     * evictable.
     */
    
    static void scan_all_zones_unevictable_pages(void)
    
    {
    	struct zone *zone;
    
    	for_each_zone(zone) {
    		scan_zone_unevictable_pages(zone);
    	}
    }
    
    /*
     * scan_unevictable_pages [vm] sysctl handler.  On demand re-scan of
     * all nodes' unevictable lists for evictable pages
     */
    unsigned long scan_unevictable_pages;
    
    int scan_unevictable_handler(struct ctl_table *table, int write,
    
    			   size_t *length, loff_t *ppos)
    {
    
    	proc_doulongvec_minmax(table, write, buffer, length, ppos);
    
    
    	if (write && *(unsigned long *)table->data)
    		scan_all_zones_unevictable_pages();
    
    	scan_unevictable_pages = 0;
    	return 0;
    }
    
    
    /*
     * per node 'scan_unevictable_pages' attribute.  On demand re-scan of
     * a specified node's per zone unevictable lists for evictable pages.
     */
    
    static ssize_t read_scan_unevictable_node(struct sys_device *dev,
    					  struct sysdev_attribute *attr,
    					  char *buf)
    {
    	return sprintf(buf, "0\n");	/* always zero; should fit... */
    }
    
    static ssize_t write_scan_unevictable_node(struct sys_device *dev,
    					   struct sysdev_attribute *attr,
    					const char *buf, size_t count)
    {
    	struct zone *node_zones = NODE_DATA(dev->id)->node_zones;
    	struct zone *zone;
    	unsigned long res;
    	unsigned long req = strict_strtoul(buf, 10, &res);
    
    	if (!req)
    		return 1;	/* zero is no-op */
    
    	for (zone = node_zones; zone - node_zones < MAX_NR_ZONES; ++zone) {
    		if (!populated_zone(zone))
    			continue;
    		scan_zone_unevictable_pages(zone);
    	}
    	return 1;
    }
    
    
    static SYSDEV_ATTR(scan_unevictable_pages, S_IRUGO | S_IWUSR,
    			read_scan_unevictable_node,
    			write_scan_unevictable_node);
    
    int scan_unevictable_register_node(struct node *node)
    {
    	return sysdev_create_file(&node->sysdev, &attr_scan_unevictable_pages);
    }
    
    void scan_unevictable_unregister_node(struct node *node)
    {
    	sysdev_remove_file(&node->sysdev, &attr_scan_unevictable_pages);
    }