/* * Frontswap, like a true swap device, may unnecessarily retain pages * under certain circumstances; "shrink" frontswap is essentially a * "partial swapoff" and works by calling try_to_unuse to attempt to * unuse enough frontswap pages to attempt to -- subject to memory * constraints -- reduce the number of pages in frontswap */ void frontswap_shrink(unsigned long target_pages) { int wrapped = 0; bool locked = false; for (wrapped = 0; wrapped <= 3; wrapped++) { struct swap_info_struct *si = NULL; unsigned long total_pages = 0, total_pages_to_unuse; unsigned long pages = 0, unuse_pages = 0; int type; /* * we don't want to hold swap_lock while doing a very * lengthy try_to_unuse, but swap_list may change * so restart scan from swap_list.head each time */ spin_lock(&swap_lock); locked = true; total_pages = 0; for (type = swap_list.head; type >= 0; type = si->next) { si = swap_info[type]; total_pages += si->frontswap_pages; } if (total_pages <= target_pages) goto out; total_pages_to_unuse = total_pages - target_pages; for (type = swap_list.head; type >= 0; type = si->next) { si = swap_info[type]; if (total_pages_to_unuse < si->frontswap_pages) pages = unuse_pages = total_pages_to_unuse; else { pages = si->frontswap_pages; unuse_pages = 0; /* unuse all */ } if (security_vm_enough_memory_kern(pages)) continue; vm_unacct_memory(pages); break; } if (type < 0) goto out; locked = false; spin_unlock(&swap_lock); try_to_unuse(type, true, unuse_pages); } out: if (locked) spin_unlock(&swap_lock); return; }
asmlinkage int sys_swapoff(const char * specialfile) { struct swap_info_struct * p; struct inode * inode; struct file filp; int i, type, prev; int err; if (!suser()) return -EPERM; err = namei(specialfile,&inode); if (err) return err; prev = -1; for (type = swap_list.head; type >= 0; type = swap_info[type].next) { p = swap_info + type; if ((p->flags & SWP_WRITEOK) == SWP_WRITEOK) { if (p->swap_file) { if (p->swap_file == inode) break; } else { if (S_ISBLK(inode->i_mode) && (p->swap_device == inode->i_rdev)) break; } } prev = type; } if (type < 0){ iput(inode); return -EINVAL; } if (prev < 0) { swap_list.head = p->next; } else { swap_info[prev].next = p->next; } if (type == swap_list.next) { /* just pick something that's safe... */ swap_list.next = swap_list.head; } p->flags = SWP_USED; err = try_to_unuse(type); if (err) { iput(inode); /* re-insert swap space back into swap_list */ for (prev = -1, i = swap_list.head; i >= 0; prev = i, i = swap_info[i].next) if (p->prio >= swap_info[i].prio) break; p->next = i; if (prev < 0) swap_list.head = swap_list.next = p - swap_info; else swap_info[prev].next = p - swap_info; p->flags = SWP_WRITEOK; return err; } if(p->swap_device){ memset(&filp, 0, sizeof(filp)); filp.f_inode = inode; filp.f_mode = 3; /* read write */ /* open it again to get fops */ if( !blkdev_open(inode, &filp) && filp.f_op && filp.f_op->release){ filp.f_op->release(inode,&filp); filp.f_op->release(inode,&filp); } } iput(inode); nr_swap_pages -= p->pages; iput(p->swap_file); p->swap_file = NULL; p->swap_device = 0; vfree(p->swap_map); p->swap_map = NULL; free_page((long) p->swap_lockmap); p->swap_lockmap = NULL; p->flags = 0; return 0; }