Skip to content

Commit 42288fe

Browse files
Mel Gormantorvalds
authored andcommitted
mm: mempolicy: Convert shared_policy mutex to spinlock
Sasha was fuzzing with trinity and reported the following problem: BUG: sleeping function called from invalid context at kernel/mutex.c:269 in_atomic(): 1, irqs_disabled(): 0, pid: 6361, name: trinity-main 2 locks held by trinity-main/6361: #0: (&mm->mmap_sem){++++++}, at: [<ffffffff810aa314>] __do_page_fault+0x1e4/0x4f0 #1: (&(&mm->page_table_lock)->rlock){+.+...}, at: [<ffffffff8122f017>] handle_pte_fault+0x3f7/0x6a0 Pid: 6361, comm: trinity-main Tainted: G W 3.7.0-rc2-next-20121024-sasha-00001-gd95ef01-dirty torvalds#74 Call Trace: __might_sleep+0x1c3/0x1e0 mutex_lock_nested+0x29/0x50 mpol_shared_policy_lookup+0x2e/0x90 shmem_get_policy+0x2e/0x30 get_vma_policy+0x5a/0xa0 mpol_misplaced+0x41/0x1d0 handle_pte_fault+0x465/0x6a0 This was triggered by a different version of automatic NUMA balancing but in theory the current version is vunerable to the same problem. do_numa_page -> numa_migrate_prep -> mpol_misplaced -> get_vma_policy -> shmem_get_policy It's very unlikely this will happen as shared pages are not marked pte_numa -- see the page_mapcount() check in change_pte_range() -- but it is possible. To address this, this patch restores sp->lock as originally implemented by Kosaki Motohiro. In the path where get_vma_policy() is called, it should not be calling sp_alloc() so it is not necessary to treat the PTL specially. Signed-off-by: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com> Tested-by: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com> Signed-off-by: Mel Gorman <mgorman@suse.de> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
1 parent 5439ca6 commit 42288fe

File tree

2 files changed

+49
-21
lines changed

2 files changed

+49
-21
lines changed

include/linux/mempolicy.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ struct sp_node {
123123

124124
struct shared_policy {
125125
struct rb_root root;
126-
struct mutex mutex;
126+
spinlock_t lock;
127127
};
128128

129129
void mpol_shared_policy_init(struct shared_policy *sp, struct mempolicy *mpol);

mm/mempolicy.c

Lines changed: 48 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2132,7 +2132,7 @@ bool __mpol_equal(struct mempolicy *a, struct mempolicy *b)
21322132
*/
21332133

21342134
/* lookup first element intersecting start-end */
2135-
/* Caller holds sp->mutex */
2135+
/* Caller holds sp->lock */
21362136
static struct sp_node *
21372137
sp_lookup(struct shared_policy *sp, unsigned long start, unsigned long end)
21382138
{
@@ -2196,13 +2196,13 @@ mpol_shared_policy_lookup(struct shared_policy *sp, unsigned long idx)
21962196

21972197
if (!sp->root.rb_node)
21982198
return NULL;
2199-
mutex_lock(&sp->mutex);
2199+
spin_lock(&sp->lock);
22002200
sn = sp_lookup(sp, idx, idx+1);
22012201
if (sn) {
22022202
mpol_get(sn->policy);
22032203
pol = sn->policy;
22042204
}
2205-
mutex_unlock(&sp->mutex);
2205+
spin_unlock(&sp->lock);
22062206
return pol;
22072207
}
22082208

@@ -2328,6 +2328,14 @@ static void sp_delete(struct shared_policy *sp, struct sp_node *n)
23282328
sp_free(n);
23292329
}
23302330

2331+
static void sp_node_init(struct sp_node *node, unsigned long start,
2332+
unsigned long end, struct mempolicy *pol)
2333+
{
2334+
node->start = start;
2335+
node->end = end;
2336+
node->policy = pol;
2337+
}
2338+
23312339
static struct sp_node *sp_alloc(unsigned long start, unsigned long end,
23322340
struct mempolicy *pol)
23332341
{
@@ -2344,10 +2352,7 @@ static struct sp_node *sp_alloc(unsigned long start, unsigned long end,
23442352
return NULL;
23452353
}
23462354
newpol->flags |= MPOL_F_SHARED;
2347-
2348-
n->start = start;
2349-
n->end = end;
2350-
n->policy = newpol;
2355+
sp_node_init(n, start, end, newpol);
23512356

23522357
return n;
23532358
}
@@ -2357,9 +2362,12 @@ static int shared_policy_replace(struct shared_policy *sp, unsigned long start,
23572362
unsigned long end, struct sp_node *new)
23582363
{
23592364
struct sp_node *n;
2365+
struct sp_node *n_new = NULL;
2366+
struct mempolicy *mpol_new = NULL;
23602367
int ret = 0;
23612368

2362-
mutex_lock(&sp->mutex);
2369+
restart:
2370+
spin_lock(&sp->lock);
23632371
n = sp_lookup(sp, start, end);
23642372
/* Take care of old policies in the same range. */
23652373
while (n && n->start < end) {
@@ -2372,14 +2380,16 @@ static int shared_policy_replace(struct shared_policy *sp, unsigned long start,
23722380
} else {
23732381
/* Old policy spanning whole new range. */
23742382
if (n->end > end) {
2375-
struct sp_node *new2;
2376-
new2 = sp_alloc(end, n->end, n->policy);
2377-
if (!new2) {
2378-
ret = -ENOMEM;
2379-
goto out;
2380-
}
2383+
if (!n_new)
2384+
goto alloc_new;
2385+
2386+
*mpol_new = *n->policy;
2387+
atomic_set(&mpol_new->refcnt, 1);
2388+
sp_node_init(n_new, n->end, end, mpol_new);
2389+
sp_insert(sp, n_new);
23812390
n->end = start;
2382-
sp_insert(sp, new2);
2391+
n_new = NULL;
2392+
mpol_new = NULL;
23832393
break;
23842394
} else
23852395
n->end = start;
@@ -2390,9 +2400,27 @@ static int shared_policy_replace(struct shared_policy *sp, unsigned long start,
23902400
}
23912401
if (new)
23922402
sp_insert(sp, new);
2393-
out:
2394-
mutex_unlock(&sp->mutex);
2403+
spin_unlock(&sp->lock);
2404+
ret = 0;
2405+
2406+
err_out:
2407+
if (mpol_new)
2408+
mpol_put(mpol_new);
2409+
if (n_new)
2410+
kmem_cache_free(sn_cache, n_new);
2411+
23952412
return ret;
2413+
2414+
alloc_new:
2415+
spin_unlock(&sp->lock);
2416+
ret = -ENOMEM;
2417+
n_new = kmem_cache_alloc(sn_cache, GFP_KERNEL);
2418+
if (!n_new)
2419+
goto err_out;
2420+
mpol_new = kmem_cache_alloc(policy_cache, GFP_KERNEL);
2421+
if (!mpol_new)
2422+
goto err_out;
2423+
goto restart;
23962424
}
23972425

23982426
/**
@@ -2410,7 +2438,7 @@ void mpol_shared_policy_init(struct shared_policy *sp, struct mempolicy *mpol)
24102438
int ret;
24112439

24122440
sp->root = RB_ROOT; /* empty tree == default mempolicy */
2413-
mutex_init(&sp->mutex);
2441+
spin_lock_init(&sp->lock);
24142442

24152443
if (mpol) {
24162444
struct vm_area_struct pvma;
@@ -2476,14 +2504,14 @@ void mpol_free_shared_policy(struct shared_policy *p)
24762504

24772505
if (!p->root.rb_node)
24782506
return;
2479-
mutex_lock(&p->mutex);
2507+
spin_lock(&p->lock);
24802508
next = rb_first(&p->root);
24812509
while (next) {
24822510
n = rb_entry(next, struct sp_node, nd);
24832511
next = rb_next(&n->nd);
24842512
sp_delete(p, n);
24852513
}
2486-
mutex_unlock(&p->mutex);
2514+
spin_unlock(&p->lock);
24872515
}
24882516

24892517
#ifdef CONFIG_NUMA_BALANCING

0 commit comments

Comments
 (0)