diff options
Diffstat (limited to 'kernel/cpuset.c')
| -rw-r--r-- | kernel/cpuset.c | 106 | 
1 files changed, 74 insertions, 32 deletions
diff --git a/kernel/cpuset.c b/kernel/cpuset.c index ba401fab459..d10946748ec 100644 --- a/kernel/cpuset.c +++ b/kernel/cpuset.c @@ -920,9 +920,6 @@ static int update_cpumask(struct cpuset *cs, struct cpuset *trialcs,   *    call to guarantee_online_mems(), as we know no one is changing   *    our task's cpuset.   * - *    Hold callback_mutex around the two modifications of our tasks - *    mems_allowed to synchronize with cpuset_mems_allowed(). - *   *    While the mm_struct we are migrating is typically from some   *    other task, the task_struct mems_allowed that we are hacking   *    is for our current task, which must allocate new pages for that @@ -973,15 +970,20 @@ static void cpuset_change_nodemask(struct task_struct *p,  	struct cpuset *cs;  	int migrate;  	const nodemask_t *oldmem = scan->data; -	nodemask_t newmems; +	NODEMASK_ALLOC(nodemask_t, newmems, GFP_KERNEL); + +	if (!newmems) +		return;  	cs = cgroup_cs(scan->cg); -	guarantee_online_mems(cs, &newmems); +	guarantee_online_mems(cs, newmems);  	task_lock(p); -	cpuset_change_task_nodemask(p, &newmems); +	cpuset_change_task_nodemask(p, newmems);  	task_unlock(p); +	NODEMASK_FREE(newmems); +  	mm = get_task_mm(p);  	if (!mm)  		return; @@ -1051,16 +1053,21 @@ static void update_tasks_nodemask(struct cpuset *cs, const nodemask_t *oldmem,  static int update_nodemask(struct cpuset *cs, struct cpuset *trialcs,  			   const char *buf)  { -	nodemask_t oldmem; +	NODEMASK_ALLOC(nodemask_t, oldmem, GFP_KERNEL);  	int retval;  	struct ptr_heap heap; +	if (!oldmem) +		return -ENOMEM; +  	/*  	 * top_cpuset.mems_allowed tracks node_stats[N_HIGH_MEMORY];  	 * it's read-only  	 */ -	if (cs == &top_cpuset) -		return -EACCES; +	if (cs == &top_cpuset) { +		retval = -EACCES; +		goto done; +	}  	/*  	 * An empty mems_allowed is ok iff there are no tasks in the cpuset. @@ -1076,11 +1083,13 @@ static int update_nodemask(struct cpuset *cs, struct cpuset *trialcs,  			goto done;  		if (!nodes_subset(trialcs->mems_allowed, -				node_states[N_HIGH_MEMORY])) -			return -EINVAL; +				node_states[N_HIGH_MEMORY])) { +			retval =  -EINVAL; +			goto done; +		}  	} -	oldmem = cs->mems_allowed; -	if (nodes_equal(oldmem, trialcs->mems_allowed)) { +	*oldmem = cs->mems_allowed; +	if (nodes_equal(*oldmem, trialcs->mems_allowed)) {  		retval = 0;		/* Too easy - nothing to do */  		goto done;  	} @@ -1096,10 +1105,11 @@ static int update_nodemask(struct cpuset *cs, struct cpuset *trialcs,  	cs->mems_allowed = trialcs->mems_allowed;  	mutex_unlock(&callback_mutex); -	update_tasks_nodemask(cs, &oldmem, &heap); +	update_tasks_nodemask(cs, oldmem, &heap);  	heap_free(&heap);  done: +	NODEMASK_FREE(oldmem);  	return retval;  } @@ -1384,40 +1394,47 @@ static void cpuset_attach(struct cgroup_subsys *ss, struct cgroup *cont,  			  struct cgroup *oldcont, struct task_struct *tsk,  			  bool threadgroup)  { -	nodemask_t from, to;  	struct mm_struct *mm;  	struct cpuset *cs = cgroup_cs(cont);  	struct cpuset *oldcs = cgroup_cs(oldcont); +	NODEMASK_ALLOC(nodemask_t, from, GFP_KERNEL); +	NODEMASK_ALLOC(nodemask_t, to, GFP_KERNEL); + +	if (from == NULL || to == NULL) +		goto alloc_fail;  	if (cs == &top_cpuset) {  		cpumask_copy(cpus_attach, cpu_possible_mask); -		to = node_possible_map;  	} else {  		guarantee_online_cpus(cs, cpus_attach); -		guarantee_online_mems(cs, &to);  	} +	guarantee_online_mems(cs, to);  	/* do per-task migration stuff possibly for each in the threadgroup */ -	cpuset_attach_task(tsk, &to, cs); +	cpuset_attach_task(tsk, to, cs);  	if (threadgroup) {  		struct task_struct *c;  		rcu_read_lock();  		list_for_each_entry_rcu(c, &tsk->thread_group, thread_group) { -			cpuset_attach_task(c, &to, cs); +			cpuset_attach_task(c, to, cs);  		}  		rcu_read_unlock();  	}  	/* change mm; only needs to be done once even if threadgroup */ -	from = oldcs->mems_allowed; -	to = cs->mems_allowed; +	*from = oldcs->mems_allowed; +	*to = cs->mems_allowed;  	mm = get_task_mm(tsk);  	if (mm) { -		mpol_rebind_mm(mm, &to); +		mpol_rebind_mm(mm, to);  		if (is_memory_migrate(cs)) -			cpuset_migrate_mm(mm, &from, &to); +			cpuset_migrate_mm(mm, from, to);  		mmput(mm);  	} + +alloc_fail: +	NODEMASK_FREE(from); +	NODEMASK_FREE(to);  }  /* The various types of files and directories in a cpuset file system */ @@ -1562,13 +1579,21 @@ static int cpuset_sprintf_cpulist(char *page, struct cpuset *cs)  static int cpuset_sprintf_memlist(char *page, struct cpuset *cs)  { -	nodemask_t mask; +	NODEMASK_ALLOC(nodemask_t, mask, GFP_KERNEL); +	int retval; + +	if (mask == NULL) +		return -ENOMEM;  	mutex_lock(&callback_mutex); -	mask = cs->mems_allowed; +	*mask = cs->mems_allowed;  	mutex_unlock(&callback_mutex); -	return nodelist_scnprintf(page, PAGE_SIZE, mask); +	retval = nodelist_scnprintf(page, PAGE_SIZE, *mask); + +	NODEMASK_FREE(mask); + +	return retval;  }  static ssize_t cpuset_common_file_read(struct cgroup *cont, @@ -1997,7 +2022,10 @@ static void scan_for_empty_cpusets(struct cpuset *root)  	struct cpuset *cp;	/* scans cpusets being updated */  	struct cpuset *child;	/* scans child cpusets of cp */  	struct cgroup *cont; -	nodemask_t oldmems; +	NODEMASK_ALLOC(nodemask_t, oldmems, GFP_KERNEL); + +	if (oldmems == NULL) +		return;  	list_add_tail((struct list_head *)&root->stack_list, &queue); @@ -2014,7 +2042,7 @@ static void scan_for_empty_cpusets(struct cpuset *root)  		    nodes_subset(cp->mems_allowed, node_states[N_HIGH_MEMORY]))  			continue; -		oldmems = cp->mems_allowed; +		*oldmems = cp->mems_allowed;  		/* Remove offline cpus and mems from this cpuset. */  		mutex_lock(&callback_mutex); @@ -2030,9 +2058,10 @@ static void scan_for_empty_cpusets(struct cpuset *root)  			remove_tasks_in_empty_cpuset(cp);  		else {  			update_tasks_cpumask(cp, NULL); -			update_tasks_nodemask(cp, &oldmems, NULL); +			update_tasks_nodemask(cp, oldmems, NULL);  		}  	} +	NODEMASK_FREE(oldmems);  }  /* @@ -2090,20 +2119,33 @@ static int cpuset_track_online_cpus(struct notifier_block *unused_nb,  static int cpuset_track_online_nodes(struct notifier_block *self,  				unsigned long action, void *arg)  { +	NODEMASK_ALLOC(nodemask_t, oldmems, GFP_KERNEL); + +	if (oldmems == NULL) +		return NOTIFY_DONE; +  	cgroup_lock();  	switch (action) {  	case MEM_ONLINE: -	case MEM_OFFLINE: +		*oldmems = top_cpuset.mems_allowed;  		mutex_lock(&callback_mutex);  		top_cpuset.mems_allowed = node_states[N_HIGH_MEMORY];  		mutex_unlock(&callback_mutex); -		if (action == MEM_OFFLINE) -			scan_for_empty_cpusets(&top_cpuset); +		update_tasks_nodemask(&top_cpuset, oldmems, NULL); +		break; +	case MEM_OFFLINE: +		/* +		 * needn't update top_cpuset.mems_allowed explicitly because +		 * scan_for_empty_cpusets() will update it. +		 */ +		scan_for_empty_cpusets(&top_cpuset);  		break;  	default:  		break;  	}  	cgroup_unlock(); + +	NODEMASK_FREE(oldmems);  	return NOTIFY_OK;  }  #endif  |