📄 swapfile.c
字号:
/* * linux/mm/swapfile.c * * Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds * Swap reorganised 29.12.95, Stephen Tweedie */#include <linux/slab.h>#include <linux/smp_lock.h>#include <linux/kernel_stat.h>#include <linux/swap.h>#include <linux/swapctl.h>#include <linux/blkdev.h> /* for blk_size */#include <linux/vmalloc.h>#include <linux/pagemap.h>#include <linux/shm.h>#include <linux/compiler.h>#include <asm/pgtable.h>spinlock_t swaplock = SPIN_LOCK_UNLOCKED;unsigned int nr_swapfiles;int total_swap_pages;static int swap_overflow;static const char Bad_file[] = "Bad swap file entry ";static const char Unused_file[] = "Unused swap file entry ";static const char Bad_offset[] = "Bad swap offset entry ";static const char Unused_offset[] = "Unused swap offset entry ";struct swap_list_t swap_list = {-1, -1};struct swap_info_struct swap_info[MAX_SWAPFILES];#define SWAPFILE_CLUSTER 256static inline int scan_swap_map(struct swap_info_struct *si){ unsigned long offset; /* * We try to cluster swap pages by allocating them * sequentially in swap. Once we've allocated * SWAPFILE_CLUSTER pages this way, however, we resort to * first-free allocation, starting a new cluster. This * prevents us from scattering swap pages all over the entire * swap partition, so that we reduce overall disk seek times * between swap pages. -- sct */ if (si->cluster_nr) { while (si->cluster_next <= si->highest_bit) { offset = si->cluster_next++; if (si->swap_map[offset]) continue; si->cluster_nr--; goto got_page; } } si->cluster_nr = SWAPFILE_CLUSTER; /* try to find an empty (even not aligned) cluster. */ offset = si->lowest_bit; check_next_cluster: if (offset+SWAPFILE_CLUSTER-1 <= si->highest_bit) { int nr; for (nr = offset; nr < offset+SWAPFILE_CLUSTER; nr++) if (si->swap_map[nr]) { offset = nr+1; goto check_next_cluster; } /* We found a completly empty cluster, so start * using it. */ goto got_page; } /* No luck, so now go finegrined as usual. -Andrea */ for (offset = si->lowest_bit; offset <= si->highest_bit ; offset++) { if (si->swap_map[offset]) continue; si->lowest_bit = offset+1; got_page: if (offset == si->lowest_bit) si->lowest_bit++; if (offset == si->highest_bit) si->highest_bit--; if (si->lowest_bit > si->highest_bit) { si->lowest_bit = si->max; si->highest_bit = 0; } si->swap_map[offset] = 1; nr_swap_pages--; si->cluster_next = offset+1; return offset; } si->lowest_bit = si->max; si->highest_bit = 0; return 0;}swp_entry_t get_swap_page(void){ struct swap_info_struct * p; unsigned long offset; swp_entry_t entry; int type, wrapped = 0; entry.val = 0; /* Out of memory */ swap_list_lock(); type = swap_list.next; if (type < 0) goto out; if (nr_swap_pages <= 0) goto out; while (1) { p = &swap_info[type]; if ((p->flags & SWP_WRITEOK) == SWP_WRITEOK) { swap_device_lock(p); offset = scan_swap_map(p); swap_device_unlock(p); if (offset) { entry = SWP_ENTRY(type,offset); type = swap_info[type].next; if (type < 0 || p->prio != swap_info[type].prio) { swap_list.next = swap_list.head; } else { swap_list.next = type; } goto out; } } type = p->next; if (!wrapped) { if (type < 0 || p->prio != swap_info[type].prio) { type = swap_list.head; wrapped = 1; } } else if (type < 0) goto out; /* out of swap space */ }out: swap_list_unlock(); return entry;}static struct swap_info_struct * swap_info_get(swp_entry_t entry){ struct swap_info_struct * p; unsigned long offset, type; if (!entry.val) goto out; type = SWP_TYPE(entry); if (type >= nr_swapfiles) goto bad_nofile; p = & swap_info[type]; if (!(p->flags & SWP_USED)) goto bad_device; offset = SWP_OFFSET(entry); if (offset >= p->max) goto bad_offset; if (!p->swap_map[offset]) goto bad_free; swap_list_lock(); if (p->prio > swap_info[swap_list.next].prio) swap_list.next = type; swap_device_lock(p); return p;bad_free: printk(KERN_ERR "swap_free: %s%08lx\n", Unused_offset, entry.val); goto out;bad_offset: printk(KERN_ERR "swap_free: %s%08lx\n", Bad_offset, entry.val); goto out;bad_device: printk(KERN_ERR "swap_free: %s%08lx\n", Unused_file, entry.val); goto out;bad_nofile: printk(KERN_ERR "swap_free: %s%08lx\n", Bad_file, entry.val);out: return NULL;} static void swap_info_put(struct swap_info_struct * p){ swap_device_unlock(p); swap_list_unlock();}static int swap_entry_free(struct swap_info_struct *p, unsigned long offset){ int count = p->swap_map[offset]; if (count < SWAP_MAP_MAX) { count--; p->swap_map[offset] = count; if (!count) { if (offset < p->lowest_bit) p->lowest_bit = offset; if (offset > p->highest_bit) p->highest_bit = offset; nr_swap_pages++; } } return count;}/* * Caller has made sure that the swapdevice corresponding to entry * is still around or has not been recycled. */void swap_free(swp_entry_t entry){ struct swap_info_struct * p; p = swap_info_get(entry); if (p) { swap_entry_free(p, SWP_OFFSET(entry)); swap_info_put(p); }}/* * Check if we're the only user of a swap page, * when the page is locked. */static int exclusive_swap_page(struct page *page){ int retval = 0; struct swap_info_struct * p; swp_entry_t entry; entry.val = page->index; p = swap_info_get(entry); if (p) { /* Is the only swap cache user the cache itself? */ if (p->swap_map[SWP_OFFSET(entry)] == 1) { /* Recheck the page count with the pagecache lock held.. */ spin_lock(&pagecache_lock); if (page_count(page) - !!page->buffers == 2) retval = 1; spin_unlock(&pagecache_lock); } swap_info_put(p); } return retval;}/* * We can use this swap cache entry directly * if there are no other references to it. * * Here "exclusive_swap_page()" does the real * work, but we opportunistically check whether * we need to get all the locks first.. */int can_share_swap_page(struct page *page){ int retval = 0; if (!PageLocked(page)) BUG(); switch (page_count(page)) { case 3: if (!page->buffers) break; /* Fallthrough */ case 2: if (!PageSwapCache(page)) break; retval = exclusive_swap_page(page); break; case 1: if (PageReserved(page)) break; retval = 1; } return retval;}/* * Work out if there are any other processes sharing this * swap cache page. Free it if you can. Return success. */int remove_exclusive_swap_page(struct page *page){ int retval; struct swap_info_struct * p; swp_entry_t entry; if (!PageLocked(page)) BUG(); if (!PageSwapCache(page)) return 0; if (page_count(page) - !!page->buffers != 2) /* 2: us + cache */ return 0; entry.val = page->index; p = swap_info_get(entry); if (!p) return 0; /* Is the only swap cache user the cache itself? */ retval = 0; if (p->swap_map[SWP_OFFSET(entry)] == 1) { /* Recheck the page count with the pagecache lock held.. */ spin_lock(&pagecache_lock); if (page_count(page) - !!page->buffers == 2) { __delete_from_swap_cache(page); SetPageDirty(page); retval = 1; } spin_unlock(&pagecache_lock); } swap_info_put(p); if (retval) { block_flushpage(page, 0); swap_free(entry); page_cache_release(page); } return retval;}/* * Free the swap entry like above, but also try to * free the page cache entry if it is the last user. */void free_swap_and_cache(swp_entry_t entry){ struct swap_info_struct * p; struct page *page = NULL; p = swap_info_get(entry); if (p) { if (swap_entry_free(p, SWP_OFFSET(entry)) == 1) page = find_trylock_page(&swapper_space, entry.val); swap_info_put(p); } if (page) { page_cache_get(page); /* Only cache user (+us), or swap space full? Free it! */ if (page_count(page) - !!page->buffers == 2 || vm_swap_full()) { delete_from_swap_cache(page); SetPageDirty(page); } UnlockPage(page); page_cache_release(page); }}/* * The swap entry has been read in advance, and we return 1 to indicate * that the page has been used or is no longer needed. * * Always set the resulting pte to be nowrite (the same as COW pages * after one process has exited). We don't know just how many PTEs will * share this swap entry, so be cautious and let do_wp_page work out * what to do if a write is requested later. *//* mmlist_lock and vma->vm_mm->page_table_lock are held */static inline void unuse_pte(struct vm_area_struct * vma, unsigned long address, pte_t *dir, swp_entry_t entry, struct page* page){ pte_t pte = *dir; if (likely(pte_to_swp_entry(pte).val != entry.val)) return; if (unlikely(pte_none(pte) || pte_present(pte))) return; get_page(page); set_pte(dir, pte_mkold(mk_pte(page, vma->vm_page_prot))); swap_free(entry); ++vma->vm_mm->rss;}/* mmlist_lock and vma->vm_mm->page_table_lock are held */static inline void unuse_pmd(struct vm_area_struct * vma, pmd_t *dir, unsigned long address, unsigned long size, unsigned long offset, swp_entry_t entry, struct page* page){ pte_t * pte; unsigned long end; if (pmd_none(*dir)) return; if (pmd_bad(*dir)) { pmd_ERROR(*dir); pmd_clear(dir); return; } pte = pte_offset(dir, address); offset += address & PMD_MASK; address &= ~PMD_MASK; end = address + size; if (end > PMD_SIZE) end = PMD_SIZE; do { unuse_pte(vma, offset+address-vma->vm_start, pte, entry, page); address += PAGE_SIZE; pte++; } while (address && (address < end));}/* mmlist_lock and vma->vm_mm->page_table_lock are held */static inline void unuse_pgd(struct vm_area_struct * vma, pgd_t *dir, unsigned long address, unsigned long size, swp_entry_t entry, struct page* page){ pmd_t * pmd; unsigned long offset, end; if (pgd_none(*dir)) return; if (pgd_bad(*dir)) { pgd_ERROR(*dir); pgd_clear(dir); return; } pmd = pmd_offset(dir, address); offset = address & PGDIR_MASK; address &= ~PGDIR_MASK; end = address + size; if (end > PGDIR_SIZE) end = PGDIR_SIZE; if (address >= end) BUG(); do {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -