/*
* 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;
}
Exemple #2
0
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;
}