📄 swapfile.c
字号:
int i, j, prev; int error; static int least_priority = 0; union swap_header *swap_header = 0; int swap_header_version; int nr_good_pages = 0; unsigned long maxpages = 1; int swapfilesize; struct block_device *bdev = NULL; unsigned short *swap_map; if (!capable(CAP_SYS_ADMIN)) return -EPERM; lock_kernel(); swap_list_lock(); p = swap_info; for (type = 0 ; type < nr_swapfiles ; type++,p++) if (!(p->flags & SWP_USED)) break; error = -EPERM; if (type >= MAX_SWAPFILES) { swap_list_unlock(); goto out; } if (type >= nr_swapfiles) nr_swapfiles = type+1; p->flags = SWP_USED; p->swap_file = NULL; p->swap_vfsmnt = NULL; p->swap_device = 0; p->swap_map = NULL; p->lowest_bit = 0; p->highest_bit = 0; p->cluster_nr = 0; p->sdev_lock = SPIN_LOCK_UNLOCKED; p->next = -1; if (swap_flags & SWAP_FLAG_PREFER) { p->prio = (swap_flags & SWAP_FLAG_PRIO_MASK)>>SWAP_FLAG_PRIO_SHIFT; } else { p->prio = --least_priority; } swap_list_unlock(); error = user_path_walk(specialfile, &nd); if (error) goto bad_swap_2; p->swap_file = nd.dentry; p->swap_vfsmnt = nd.mnt; swap_inode = nd.dentry->d_inode; error = -EINVAL; if (S_ISBLK(swap_inode->i_mode)) { kdev_t dev = swap_inode->i_rdev; struct block_device_operations *bdops; devfs_handle_t de; p->swap_device = dev; set_blocksize(dev, PAGE_SIZE); bd_acquire(swap_inode); bdev = swap_inode->i_bdev; de = devfs_get_handle_from_inode(swap_inode); bdops = devfs_get_ops(de); /* Increments module use count */ if (bdops) bdev->bd_op = bdops; error = blkdev_get(bdev, FMODE_READ|FMODE_WRITE, 0, BDEV_SWAP); devfs_put_ops(de);/*Decrement module use count now we're safe*/ if (error) goto bad_swap_2; set_blocksize(dev, PAGE_SIZE); error = -ENODEV; if (!dev || (blk_size[MAJOR(dev)] && !blk_size[MAJOR(dev)][MINOR(dev)])) goto bad_swap; swapfilesize = 0; if (blk_size[MAJOR(dev)]) swapfilesize = blk_size[MAJOR(dev)][MINOR(dev)] >> (PAGE_SHIFT - 10); } else if (S_ISREG(swap_inode->i_mode)) swapfilesize = swap_inode->i_size >> PAGE_SHIFT; else goto bad_swap; error = -EBUSY; for (i = 0 ; i < nr_swapfiles ; i++) { struct swap_info_struct *q = &swap_info[i]; if (i == type || !q->swap_file) continue; if (swap_inode->i_mapping == q->swap_file->d_inode->i_mapping) goto bad_swap; } swap_header = (void *) __get_free_page(GFP_USER); if (!swap_header) { printk("Unable to start swapping: out of memory :-)\n"); error = -ENOMEM; goto bad_swap; } lock_page(virt_to_page(swap_header)); rw_swap_page_nolock(READ, SWP_ENTRY(type,0), (char *) swap_header); if (!memcmp("SWAP-SPACE",swap_header->magic.magic,10)) swap_header_version = 1; else if (!memcmp("SWAPSPACE2",swap_header->magic.magic,10)) swap_header_version = 2; else { printk("Unable to find swap-space signature\n"); error = -EINVAL; goto bad_swap; } switch (swap_header_version) { case 1: memset(((char *) swap_header)+PAGE_SIZE-10,0,10); j = 0; p->lowest_bit = 0; p->highest_bit = 0; for (i = 1 ; i < 8*PAGE_SIZE ; i++) { if (test_bit(i,(char *) swap_header)) { if (!p->lowest_bit) p->lowest_bit = i; p->highest_bit = i; maxpages = i+1; j++; } } nr_good_pages = j; p->swap_map = vmalloc(maxpages * sizeof(short)); if (!p->swap_map) { error = -ENOMEM; goto bad_swap; } for (i = 1 ; i < maxpages ; i++) { if (test_bit(i,(char *) swap_header)) p->swap_map[i] = 0; else p->swap_map[i] = SWAP_MAP_BAD; } break; case 2: /* Check the swap header's sub-version and the size of the swap file and bad block lists */ if (swap_header->info.version != 1) { printk(KERN_WARNING "Unable to handle swap header version %d\n", swap_header->info.version); error = -EINVAL; goto bad_swap; } p->lowest_bit = 1; maxpages = SWP_OFFSET(SWP_ENTRY(0,~0UL)) - 1; if (maxpages > swap_header->info.last_page) maxpages = swap_header->info.last_page; p->highest_bit = maxpages - 1; error = -EINVAL; if (swap_header->info.nr_badpages > MAX_SWAP_BADPAGES) goto bad_swap; /* OK, set up the swap map and apply the bad block list */ if (!(p->swap_map = vmalloc(maxpages * sizeof(short)))) { error = -ENOMEM; goto bad_swap; } error = 0; memset(p->swap_map, 0, maxpages * sizeof(short)); for (i=0; i<swap_header->info.nr_badpages; i++) { int page = swap_header->info.badpages[i]; if (page <= 0 || page >= swap_header->info.last_page) error = -EINVAL; else p->swap_map[page] = SWAP_MAP_BAD; } nr_good_pages = swap_header->info.last_page - swap_header->info.nr_badpages - 1 /* header page */; if (error) goto bad_swap; } if (swapfilesize && maxpages > swapfilesize) { printk(KERN_WARNING "Swap area shorter than signature indicates\n"); error = -EINVAL; goto bad_swap; } if (!nr_good_pages) { printk(KERN_WARNING "Empty swap-file\n"); error = -EINVAL; goto bad_swap; } p->swap_map[0] = SWAP_MAP_BAD; swap_list_lock(); swap_device_lock(p); p->max = maxpages; p->flags = SWP_WRITEOK; p->pages = nr_good_pages; nr_swap_pages += nr_good_pages; total_swap_pages += nr_good_pages; printk(KERN_INFO "Adding Swap: %dk swap-space (priority %d)\n", nr_good_pages<<(PAGE_SHIFT-10), p->prio); /* insert swap space into swap_list: */ prev = -1; for (i = swap_list.head; i >= 0; i = swap_info[i].next) { if (p->prio >= swap_info[i].prio) { break; } prev = i; } p->next = i; if (prev < 0) { swap_list.head = swap_list.next = p - swap_info; } else { swap_info[prev].next = p - swap_info; } swap_device_unlock(p); swap_list_unlock(); error = 0; goto out;bad_swap: if (bdev) blkdev_put(bdev, BDEV_SWAP);bad_swap_2: swap_list_lock(); swap_map = p->swap_map; nd.mnt = p->swap_vfsmnt; nd.dentry = p->swap_file; p->swap_device = 0; p->swap_file = NULL; p->swap_vfsmnt = NULL; p->swap_map = NULL; p->flags = 0; if (!(swap_flags & SWAP_FLAG_PREFER)) ++least_priority; swap_list_unlock(); if (swap_map) vfree(swap_map); path_release(&nd);out: if (swap_header) free_page((long) swap_header); unlock_kernel(); return error;}void si_swapinfo(struct sysinfo *val){ unsigned int i; unsigned long nr_to_be_unused = 0; swap_list_lock(); for (i = 0; i < nr_swapfiles; i++) { unsigned int j; if (swap_info[i].flags != SWP_USED) continue; for (j = 0; j < swap_info[i].max; ++j) { switch (swap_info[i].swap_map[j]) { case 0: case SWAP_MAP_BAD: continue; default: nr_to_be_unused++; } } } val->freeswap = nr_swap_pages + nr_to_be_unused; val->totalswap = total_swap_pages + nr_to_be_unused; swap_list_unlock();}/* * Verify that a swap entry is valid and increment its swap map count. * * Note: if swap_map[] reaches SWAP_MAP_MAX the entries are treated as * "permanent", but will be reclaimed by the next swapoff. */int swap_duplicate(swp_entry_t entry){ struct swap_info_struct * p; unsigned long offset, type; int result = 0; type = SWP_TYPE(entry); if (type >= nr_swapfiles) goto bad_file; p = type + swap_info; offset = SWP_OFFSET(entry); swap_device_lock(p); if (offset < p->max && p->swap_map[offset]) { if (p->swap_map[offset] < SWAP_MAP_MAX - 1) { p->swap_map[offset]++; result = 1; } else if (p->swap_map[offset] <= SWAP_MAP_MAX) { if (swap_overflow++ < 5) printk(KERN_WARNING "swap_dup: swap entry overflow\n"); p->swap_map[offset] = SWAP_MAP_MAX; result = 1; } } swap_device_unlock(p);out: return result;bad_file: printk(KERN_ERR "swap_dup: %s%08lx\n", Bad_file, entry.val); goto out;}/* * Page lock needs to be held in all cases to prevent races with * swap file deletion. */int swap_count(struct page *page){ struct swap_info_struct * p; unsigned long offset, type; swp_entry_t entry; int retval = 0; entry.val = page->index; if (!entry.val) goto bad_entry; type = SWP_TYPE(entry); if (type >= nr_swapfiles) goto bad_file; p = type + swap_info; offset = SWP_OFFSET(entry); if (offset >= p->max) goto bad_offset; if (!p->swap_map[offset]) goto bad_unused; retval = p->swap_map[offset];out: return retval;bad_entry: printk(KERN_ERR "swap_count: null entry!\n"); goto out;bad_file: printk(KERN_ERR "swap_count: %s%08lx\n", Bad_file, entry.val); goto out;bad_offset: printk(KERN_ERR "swap_count: %s%08lx\n", Bad_offset, entry.val); goto out;bad_unused: printk(KERN_ERR "swap_count: %s%08lx\n", Unused_offset, entry.val); goto out;}/* * Prior swap_duplicate protects against swap device deletion. */void get_swaphandle_info(swp_entry_t entry, unsigned long *offset, kdev_t *dev, struct inode **swapf){ unsigned long type; struct swap_info_struct *p; type = SWP_TYPE(entry); if (type >= nr_swapfiles) { printk(KERN_ERR "rw_swap_page: %s%08lx\n", Bad_file, entry.val); return; } p = &swap_info[type]; *offset = SWP_OFFSET(entry); if (*offset >= p->max && *offset != 0) { printk(KERN_ERR "rw_swap_page: %s%08lx\n", Bad_offset, entry.val); return; } if (p->swap_map && !p->swap_map[*offset]) { printk(KERN_ERR "rw_swap_page: %s%08lx\n", Unused_offset, entry.val); return; } if (!(p->flags & SWP_USED)) { printk(KERN_ERR "rw_swap_page: %s%08lx\n", Unused_file, entry.val); return; } if (p->swap_device) { *dev = p->swap_device; } else if (p->swap_file) { *swapf = p->swap_file->d_inode; } else { printk(KERN_ERR "rw_swap_page: no swap file or device\n"); } return;}/* * swap_device_lock prevents swap_map being freed. Don't grab an extra * reference on the swaphandle, it doesn't matter if it becomes unused. */int valid_swaphandles(swp_entry_t entry, unsigned long *offset){ int ret = 0, i = 1 << page_cluster; unsigned long toff; struct swap_info_struct *swapdev = SWP_TYPE(entry) + swap_info; if (!page_cluster) /* no readahead */ return 0; toff = (SWP_OFFSET(entry) >> page_cluster) << page_cluster; if (!toff) /* first page is swap header */ toff++, i--; *offset = toff; swap_device_lock(swapdev); do { /* Don't read-ahead past the end of the swap area */ if (toff >= swapdev->max) break; /* Don't read in free or bad pages */ if (!swapdev->swap_map[toff]) break; if (swapdev->swap_map[toff] == SWAP_MAP_BAD) break; toff++; ret++; } while (--i); swap_device_unlock(swapdev); return ret;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -