Esempio n. 1
0
/**
 * cmm_free_pages - Free pages and mark them as active
 * @nr:	number of pages to free
 *
 * Return value:
 * 	number of pages requested to be freed which were not
 **/
static long cmm_free_pages(long nr)
{
	struct cmm_page_array *pa;
	unsigned long addr;

	cmm_dbg("Begin free of %ld pages.\n", nr);
	spin_lock(&cmm_lock);
	pa = cmm_page_list;
	while (nr) {
		if (!pa || pa->index <= 0)
			break;
		addr = pa->page[--pa->index];

		if (pa->index == 0) {
			pa = pa->next;
			free_page((unsigned long) cmm_page_list);
			cmm_page_list = pa;
		}

		plpar_page_set_active(__pa(addr));
		free_page(addr);
		loaned_pages--;
		nr--;
		totalram_pages++;
	}
	spin_unlock(&cmm_lock);
	cmm_dbg("End request with %ld pages unfulfilled\n", nr);
	return nr;
}
Esempio n. 2
0
/**
 * cmm_alloc_pages - Allocate pages and mark them as loaned
 * @nr:	number of pages to allocate
 *
 * Return value:
 * 	number of pages requested to be allocated which were not
 **/
static long cmm_alloc_pages(long nr)
{
	struct cmm_page_array *pa, *npa;
	unsigned long addr;
	long rc;

	cmm_dbg("Begin request for %ld pages\n", nr);

	while (nr) {
		addr = __get_free_page(GFP_NOIO | __GFP_NOWARN |
				       __GFP_NORETRY | __GFP_NOMEMALLOC);
		if (!addr)
			break;
		spin_lock(&cmm_lock);
		pa = cmm_page_list;
		if (!pa || pa->index >= CMM_NR_PAGES) {
			/* Need a new page for the page list. */
			spin_unlock(&cmm_lock);
			npa = (struct cmm_page_array *)__get_free_page(GFP_NOIO | __GFP_NOWARN |
								       __GFP_NORETRY | __GFP_NOMEMALLOC);
			if (!npa) {
				pr_info("%s: Can not allocate new page list\n", __func__);
				free_page(addr);
				break;
			}
			spin_lock(&cmm_lock);
			pa = cmm_page_list;

			if (!pa || pa->index >= CMM_NR_PAGES) {
				npa->next = pa;
				npa->index = 0;
				pa = npa;
				cmm_page_list = pa;
			} else
				free_page((unsigned long) npa);
		}

		if ((rc = plpar_page_set_loaned(__pa(addr)))) {
			pr_err("%s: Can not set page to loaned. rc=%ld\n", __func__, rc);
			spin_unlock(&cmm_lock);
			free_page(addr);
			break;
		}

		pa->page[pa->index++] = addr;
		loaned_pages++;
		totalram_pages--;
		spin_unlock(&cmm_lock);
		nr--;
	}

	cmm_dbg("End request with %ld pages unfulfilled\n", nr);
	return nr;
}
Esempio n. 3
0
/**
 * cmm_oom_notify - OOM notifier
 * @self:	notifier block struct
 * @dummy:	not used
 * @parm:	returned - number of pages freed
 *
 * Return value:
 * 	NOTIFY_OK
 **/
static int cmm_oom_notify(struct notifier_block *self,
			  unsigned long dummy, void *parm)
{
	unsigned long *freed = parm;
	long nr = KB2PAGES(oom_kb);

	cmm_dbg("OOM processing started\n");
	nr = cmm_free_pages(nr);
	loaned_pages_target = loaned_pages;
	*freed += KB2PAGES(oom_kb) - nr;
	oom_freed_pages += KB2PAGES(oom_kb) - nr;
	cmm_dbg("OOM processing complete\n");
	return NOTIFY_OK;
}
Esempio n. 4
0
/**
 * cmm_get_mpp - Read memory performance parameters
 *
 * Makes hcall to query the current page loan request from the hypervisor.
 *
 * Return value:
 * 	nothing
 **/
static void cmm_get_mpp(void)
{
	int rc;
	struct hvcall_mpp_data mpp_data;
	unsigned long active_pages_target;
	signed long page_loan_request;

	rc = h_get_mpp(&mpp_data);

	if (rc != H_SUCCESS)
		return;

	page_loan_request = div_s64((s64)mpp_data.loan_request, PAGE_SIZE);
	loaned_pages_target = page_loan_request + loaned_pages;
	if (loaned_pages_target > oom_freed_pages)
		loaned_pages_target -= oom_freed_pages;
	else
		loaned_pages_target = 0;

	active_pages_target = totalram_pages + loaned_pages - loaned_pages_target;

	if ((min_mem_mb * 1024 * 1024) > (active_pages_target * PAGE_SIZE))
		loaned_pages_target = totalram_pages + loaned_pages -
			((min_mem_mb * 1024 * 1024) / PAGE_SIZE);

	cmm_dbg("delta = %ld, loaned = %lu, target = %lu, oom = %lu, totalram = %lu\n",
		page_loan_request, loaned_pages, loaned_pages_target,
		oom_freed_pages, totalram_pages);
}
Esempio n. 5
0
File: cmm.c Progetto: 03199618/linux
/**
 * cmm_thread - CMM task thread
 * @dummy:	not used
 *
 * Return value:
 * 	0
 **/
static int cmm_thread(void *dummy)
{
	unsigned long timeleft;

	while (1) {
		timeleft = msleep_interruptible(delay * 1000);

		if (kthread_should_stop() || timeleft)
			break;

		if (mutex_trylock(&hotplug_mutex)) {
			if (hotplug_occurred) {
				hotplug_occurred = 0;
				mutex_unlock(&hotplug_mutex);
				cmm_dbg("Hotplug operation has occurred, "
						"loaning activity suspended "
						"for %d seconds.\n",
						hotplug_delay);
				timeleft = msleep_interruptible(hotplug_delay *
						1000);
				if (kthread_should_stop() || timeleft)
					break;
				continue;
			}
			mutex_unlock(&hotplug_mutex);
		} else {
			cmm_dbg("Hotplug operation in progress, activity "
					"suspended\n");
			continue;
		}

		cmm_get_mpp();

		if (loaned_pages_target > loaned_pages) {
			if (cmm_alloc_pages(loaned_pages_target - loaned_pages))
				loaned_pages_target = loaned_pages;
		} else if (loaned_pages_target < loaned_pages)
			cmm_free_pages(loaned_pages - loaned_pages_target);
	}
	return 0;
}
Esempio n. 6
0
File: cmm.c Progetto: 03199618/linux
/**
 * cmm_get_mpp - Read memory performance parameters
 *
 * Makes hcall to query the current page loan request from the hypervisor.
 *
 * Return value:
 * 	nothing
 **/
static void cmm_get_mpp(void)
{
	int rc;
	struct hvcall_mpp_data mpp_data;
	signed long active_pages_target, page_loan_request, target;
	signed long total_pages = totalram_pages + loaned_pages;
	signed long min_mem_pages = (min_mem_mb * 1024 * 1024) / PAGE_SIZE;

	rc = h_get_mpp(&mpp_data);

	if (rc != H_SUCCESS)
		return;

	page_loan_request = div_s64((s64)mpp_data.loan_request, PAGE_SIZE);
	target = page_loan_request + (signed long)loaned_pages;

	if (target < 0 || total_pages < min_mem_pages)
		target = 0;

	if (target > oom_freed_pages)
		target -= oom_freed_pages;
	else
		target = 0;

	active_pages_target = total_pages - target;

	if (min_mem_pages > active_pages_target)
		target = total_pages - min_mem_pages;

	if (target < 0)
		target = 0;

	loaned_pages_target = target;

	cmm_dbg("delta = %ld, loaned = %lu, target = %lu, oom = %lu, totalram = %lu\n",
		page_loan_request, loaned_pages, loaned_pages_target,
		oom_freed_pages, totalram_pages);
}
Esempio n. 7
0
File: cmm.c Progetto: 03199618/linux
/**
 * cmm_memory_cb - Handle memory hotplug notifier calls
 * @self:	notifier block struct
 * @action:	action to take
 * @arg:	struct memory_notify data for handler
 *
 * Return value:
 *	NOTIFY_OK or notifier error based on subfunction return value
 *
 **/
static int cmm_memory_cb(struct notifier_block *self,
			unsigned long action, void *arg)
{
	int ret = 0;

	switch (action) {
	case MEM_GOING_OFFLINE:
		mutex_lock(&hotplug_mutex);
		hotplug_occurred = 1;
		ret = cmm_mem_going_offline(arg);
		break;
	case MEM_OFFLINE:
	case MEM_CANCEL_OFFLINE:
		mutex_unlock(&hotplug_mutex);
		cmm_dbg("Memory offline operation complete.\n");
		break;
	case MEM_GOING_ONLINE:
	case MEM_ONLINE:
	case MEM_CANCEL_ONLINE:
		break;
	}

	return notifier_from_errno(ret);
}
Esempio n. 8
0
File: cmm.c Progetto: 03199618/linux
/**
 * cmm_mem_going_offline - Unloan pages where memory is to be removed
 * @arg: memory_notify structure with page range to be offlined
 *
 * Return value:
 *	0 on success
 **/
static int cmm_mem_going_offline(void *arg)
{
	struct memory_notify *marg = arg;
	unsigned long start_page = (unsigned long)pfn_to_kaddr(marg->start_pfn);
	unsigned long end_page = start_page + (marg->nr_pages << PAGE_SHIFT);
	struct cmm_page_array *pa_curr, *pa_last, *npa;
	unsigned long idx;
	unsigned long freed = 0;

	cmm_dbg("Memory going offline, searching 0x%lx (%ld pages).\n",
			start_page, marg->nr_pages);
	spin_lock(&cmm_lock);

	/* Search the page list for pages in the range to be offlined */
	pa_last = pa_curr = cmm_page_list;
	while (pa_curr) {
		for (idx = (pa_curr->index - 1); (idx + 1) > 0; idx--) {
			if ((pa_curr->page[idx] < start_page) ||
			    (pa_curr->page[idx] >= end_page))
				continue;

			plpar_page_set_active(__pa(pa_curr->page[idx]));
			free_page(pa_curr->page[idx]);
			freed++;
			loaned_pages--;
			totalram_pages++;
			pa_curr->page[idx] = pa_last->page[--pa_last->index];
			if (pa_last->index == 0) {
				if (pa_curr == pa_last)
					pa_curr = pa_last->next;
				pa_last = pa_last->next;
				free_page((unsigned long)cmm_page_list);
				cmm_page_list = pa_last;
				continue;
			}
		}
		pa_curr = pa_curr->next;
	}

	/* Search for page list structures in the range to be offlined */
	pa_last = NULL;
	pa_curr = cmm_page_list;
	while (pa_curr) {
		if (((unsigned long)pa_curr >= start_page) &&
				((unsigned long)pa_curr < end_page)) {
			npa = (struct cmm_page_array *)__get_free_page(
					GFP_NOIO | __GFP_NOWARN |
					__GFP_NORETRY | __GFP_NOMEMALLOC);
			if (!npa) {
				spin_unlock(&cmm_lock);
				cmm_dbg("Failed to allocate memory for list "
						"management. Memory hotplug "
						"failed.\n");
				return ENOMEM;
			}
			memcpy(npa, pa_curr, PAGE_SIZE);
			if (pa_curr == cmm_page_list)
				cmm_page_list = npa;
			if (pa_last)
				pa_last->next = npa;
			free_page((unsigned long) pa_curr);
			freed++;
			pa_curr = npa;
		}

		pa_last = pa_curr;
		pa_curr = pa_curr->next;
	}

	spin_unlock(&cmm_lock);
	cmm_dbg("Released %ld pages in the search range.\n", freed);

	return 0;
}