diff options
Diffstat (limited to 'kernel')
-rw-r--r-- | kernel/perf_counter.c | 128 |
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 | */ | ||
130 | static 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 | */ | ||
164 | static 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 | |||
178 | static 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 | } |