aboutsummaryrefslogtreecommitdiffstats
path: root/kernel
diff options
context:
space:
mode:
authorPaul Mackerras <paulus@samba.org>2009-06-01 03:48:12 -0400
committerIngo Molnar <mingo@elte.hu>2009-06-01 04:04:05 -0400
commit25346b93ca079080c9cb23331db5c4f6404e8530 (patch)
treea444ca96e0c7e32d6c573e680d9365a808f48bfb /kernel
parent23db9f430be9325a861c7762c1ffadad9ca528a8 (diff)
perf_counter: Provide functions for locking and pinning the context for a task
This abstracts out the code for locking the context associated with a task. Because the context might get transferred from one task to another concurrently, we have to check after locking the context that it is still the right context for the task and retry if not. This was open-coded in find_get_context() and perf_counter_init_task(). This adds a further function for pinning the context for a task, i.e. marking it so it can't be transferred to another task. This adds a 'pin_count' field to struct perf_counter_context to indicate that a context is pinned, instead of the previous method of setting the parent_gen count to all 1s. Pinning the context with a pin_count is easier to undo and doesn't require saving the parent_gen value. This also adds a perf_unpin_context() to undo the effect of perf_pin_task_context() and changes perf_counter_init_task to use it. Signed-off-by: Paul Mackerras <paulus@samba.org> Cc: Peter Zijlstra <a.p.zijlstra@chello.nl> Cc: Mike Galbraith <efault@gmx.de> Cc: Paul Mackerras <paulus@samba.org> Cc: Corey Ashford <cjashfor@linux.vnet.ibm.com> Cc: Marcelo Tosatti <mtosatti@redhat.com> Cc: Arnaldo Carvalho de Melo <acme@redhat.com> Cc: John Kacur <jkacur@redhat.com> LKML-Reference: <18979.34748.755674.596386@cargo.ozlabs.ibm.com> Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'kernel')
-rw-r--r--kernel/perf_counter.c128
1 files changed, 74 insertions, 54 deletions
diff --git a/kernel/perf_counter.c b/kernel/perf_counter.c
index 79c3f26541d3..da8dfef4b472 100644
--- a/kernel/perf_counter.c
+++ b/kernel/perf_counter.c
@@ -123,6 +123,69 @@ static void put_ctx(struct perf_counter_context *ctx)
123} 123}
124 124
125/* 125/*
126 * Get the perf_counter_context for a task and lock it.
127 * This has to cope with with the fact that until it is locked,
128 * the context could get moved to another task.
129 */
130static struct perf_counter_context *perf_lock_task_context(
131 struct task_struct *task, unsigned long *flags)
132{
133 struct perf_counter_context *ctx;
134
135 rcu_read_lock();
136 retry:
137 ctx = rcu_dereference(task->perf_counter_ctxp);
138 if (ctx) {
139 /*
140 * If this context is a clone of another, it might
141 * get swapped for another underneath us by
142 * perf_counter_task_sched_out, though the
143 * rcu_read_lock() protects us from any context
144 * getting freed. Lock the context and check if it
145 * got swapped before we could get the lock, and retry
146 * if so. If we locked the right context, then it
147 * can't get swapped on us any more.
148 */
149 spin_lock_irqsave(&ctx->lock, *flags);
150 if (ctx != rcu_dereference(task->perf_counter_ctxp)) {
151 spin_unlock_irqrestore(&ctx->lock, *flags);
152 goto retry;
153 }
154 }
155 rcu_read_unlock();
156 return ctx;
157}
158
159/*
160 * Get the context for a task and increment its pin_count so it
161 * can't get swapped to another task. This also increments its
162 * reference count so that the context can't get freed.
163 */
164static struct perf_counter_context *perf_pin_task_context(struct task_struct *task)
165{
166 struct perf_counter_context *ctx;
167 unsigned long flags;
168
169 ctx = perf_lock_task_context(task, &flags);
170 if (ctx) {
171 ++ctx->pin_count;
172 get_ctx(ctx);
173 spin_unlock_irqrestore(&ctx->lock, flags);
174 }
175 return ctx;
176}
177
178static void perf_unpin_context(struct perf_counter_context *ctx)
179{
180 unsigned long flags;
181
182 spin_lock_irqsave(&ctx->lock, flags);
183 --ctx->pin_count;
184 spin_unlock_irqrestore(&ctx->lock, flags);
185 put_ctx(ctx);
186}
187
188/*
126 * Add a counter from the lists for its context. 189 * Add a counter from the lists for its context.
127 * Must be called with ctx->mutex and ctx->lock held. 190 * Must be called with ctx->mutex and ctx->lock held.
128 */ 191 */
@@ -916,7 +979,7 @@ static int context_equiv(struct perf_counter_context *ctx1,
916{ 979{
917 return ctx1->parent_ctx && ctx1->parent_ctx == ctx2->parent_ctx 980 return ctx1->parent_ctx && ctx1->parent_ctx == ctx2->parent_ctx
918 && ctx1->parent_gen == ctx2->parent_gen 981 && ctx1->parent_gen == ctx2->parent_gen
919 && ctx1->parent_gen != ~0ull; 982 && !ctx1->pin_count && !ctx2->pin_count;
920} 983}
921 984
922/* 985/*
@@ -1271,6 +1334,7 @@ static struct perf_counter_context *find_get_context(pid_t pid, int cpu)
1271 struct perf_counter_context *ctx; 1334 struct perf_counter_context *ctx;
1272 struct perf_counter_context *parent_ctx; 1335 struct perf_counter_context *parent_ctx;
1273 struct task_struct *task; 1336 struct task_struct *task;
1337 unsigned long flags;
1274 int err; 1338 int err;
1275 1339
1276 /* 1340 /*
@@ -1323,28 +1387,9 @@ static struct perf_counter_context *find_get_context(pid_t pid, int cpu)
1323 if (!ptrace_may_access(task, PTRACE_MODE_READ)) 1387 if (!ptrace_may_access(task, PTRACE_MODE_READ))
1324 goto errout; 1388 goto errout;
1325 1389
1326 retry_lock:
1327 rcu_read_lock();
1328 retry: 1390 retry:
1329 ctx = rcu_dereference(task->perf_counter_ctxp); 1391 ctx = perf_lock_task_context(task, &flags);
1330 if (ctx) { 1392 if (ctx) {
1331 /*
1332 * If this context is a clone of another, it might
1333 * get swapped for another underneath us by
1334 * perf_counter_task_sched_out, though the
1335 * rcu_read_lock() protects us from any context
1336 * getting freed. Lock the context and check if it
1337 * got swapped before we could get the lock, and retry
1338 * if so. If we locked the right context, then it
1339 * can't get swapped on us any more and we can
1340 * unclone it if necessary.
1341 * Once it's not a clone things will be stable.
1342 */
1343 spin_lock_irq(&ctx->lock);
1344 if (ctx != rcu_dereference(task->perf_counter_ctxp)) {
1345 spin_unlock_irq(&ctx->lock);
1346 goto retry;
1347 }
1348 parent_ctx = ctx->parent_ctx; 1393 parent_ctx = ctx->parent_ctx;
1349 if (parent_ctx) { 1394 if (parent_ctx) {
1350 put_ctx(parent_ctx); 1395 put_ctx(parent_ctx);
@@ -1355,9 +1400,8 @@ static struct perf_counter_context *find_get_context(pid_t pid, int cpu)
1355 * this context won't get freed if the task exits. 1400 * this context won't get freed if the task exits.
1356 */ 1401 */
1357 get_ctx(ctx); 1402 get_ctx(ctx);
1358 spin_unlock_irq(&ctx->lock); 1403 spin_unlock_irqrestore(&ctx->lock, flags);
1359 } 1404 }
1360 rcu_read_unlock();
1361 1405
1362 if (!ctx) { 1406 if (!ctx) {
1363 ctx = kmalloc(sizeof(struct perf_counter_context), GFP_KERNEL); 1407 ctx = kmalloc(sizeof(struct perf_counter_context), GFP_KERNEL);
@@ -1372,7 +1416,7 @@ static struct perf_counter_context *find_get_context(pid_t pid, int cpu)
1372 * the context they set. 1416 * the context they set.
1373 */ 1417 */
1374 kfree(ctx); 1418 kfree(ctx);
1375 goto retry_lock; 1419 goto retry;
1376 } 1420 }
1377 get_task_struct(task); 1421 get_task_struct(task);
1378 } 1422 }
@@ -3667,7 +3711,6 @@ int perf_counter_init_task(struct task_struct *child)
3667 struct perf_counter *counter; 3711 struct perf_counter *counter;
3668 struct task_struct *parent = current; 3712 struct task_struct *parent = current;
3669 int inherited_all = 1; 3713 int inherited_all = 1;
3670 u64 cloned_gen;
3671 int ret = 0; 3714 int ret = 0;
3672 3715
3673 child->perf_counter_ctxp = NULL; 3716 child->perf_counter_ctxp = NULL;
@@ -3693,32 +3736,17 @@ int perf_counter_init_task(struct task_struct *child)
3693 get_task_struct(child); 3736 get_task_struct(child);
3694 3737
3695 /* 3738 /*
3696 * If the parent's context is a clone, temporarily set its 3739 * If the parent's context is a clone, pin it so it won't get
3697 * parent_gen to an impossible value (all 1s) so it won't get 3740 * swapped under us.
3698 * swapped under us. The rcu_read_lock makes sure that
3699 * parent_ctx continues to exist even if it gets swapped to
3700 * another process and then freed while we are trying to get
3701 * its lock.
3702 */ 3741 */
3703 rcu_read_lock(); 3742 parent_ctx = perf_pin_task_context(parent);
3704 retry: 3743
3705 parent_ctx = rcu_dereference(parent->perf_counter_ctxp);
3706 /* 3744 /*
3707 * No need to check if parent_ctx != NULL here; since we saw 3745 * No need to check if parent_ctx != NULL here; since we saw
3708 * it non-NULL earlier, the only reason for it to become NULL 3746 * it non-NULL earlier, the only reason for it to become NULL
3709 * is if we exit, and since we're currently in the middle of 3747 * is if we exit, and since we're currently in the middle of
3710 * a fork we can't be exiting at the same time. 3748 * a fork we can't be exiting at the same time.
3711 */ 3749 */
3712 spin_lock_irq(&parent_ctx->lock);
3713 if (parent_ctx != rcu_dereference(parent->perf_counter_ctxp)) {
3714 spin_unlock_irq(&parent_ctx->lock);
3715 goto retry;
3716 }
3717 cloned_gen = parent_ctx->parent_gen;
3718 if (parent_ctx->parent_ctx)
3719 parent_ctx->parent_gen = ~0ull;
3720 spin_unlock_irq(&parent_ctx->lock);
3721 rcu_read_unlock();
3722 3750
3723 /* 3751 /*
3724 * Lock the parent list. No need to lock the child - not PID 3752 * Lock the parent list. No need to lock the child - not PID
@@ -3759,7 +3787,7 @@ int perf_counter_init_task(struct task_struct *child)
3759 cloned_ctx = rcu_dereference(parent_ctx->parent_ctx); 3787 cloned_ctx = rcu_dereference(parent_ctx->parent_ctx);
3760 if (cloned_ctx) { 3788 if (cloned_ctx) {
3761 child_ctx->parent_ctx = cloned_ctx; 3789 child_ctx->parent_ctx = cloned_ctx;
3762 child_ctx->parent_gen = cloned_gen; 3790 child_ctx->parent_gen = parent_ctx->parent_gen;
3763 } else { 3791 } else {
3764 child_ctx->parent_ctx = parent_ctx; 3792 child_ctx->parent_ctx = parent_ctx;
3765 child_ctx->parent_gen = parent_ctx->generation; 3793 child_ctx->parent_gen = parent_ctx->generation;
@@ -3769,15 +3797,7 @@ int perf_counter_init_task(struct task_struct *child)
3769 3797
3770 mutex_unlock(&parent_ctx->mutex); 3798 mutex_unlock(&parent_ctx->mutex);
3771 3799
3772 /* 3800 perf_unpin_context(parent_ctx);
3773 * Restore the clone status of the parent.
3774 */
3775 if (parent_ctx->parent_ctx) {
3776 spin_lock_irq(&parent_ctx->lock);
3777 if (parent_ctx->parent_ctx)
3778 parent_ctx->parent_gen = cloned_gen;
3779 spin_unlock_irq(&parent_ctx->lock);
3780 }
3781 3801
3782 return ret; 3802 return ret;
3783} 3803}