diff options
Diffstat (limited to 'kernel/taskstats.c')
-rw-r--r-- | kernel/taskstats.c | 98 |
1 files changed, 66 insertions, 32 deletions
diff --git a/kernel/taskstats.c b/kernel/taskstats.c index ea9506de3b85..4a0a5022b299 100644 --- a/kernel/taskstats.c +++ b/kernel/taskstats.c | |||
@@ -132,46 +132,79 @@ static int fill_pid(pid_t pid, struct task_struct *pidtsk, | |||
132 | static int fill_tgid(pid_t tgid, struct task_struct *tgidtsk, | 132 | static int fill_tgid(pid_t tgid, struct task_struct *tgidtsk, |
133 | struct taskstats *stats) | 133 | struct taskstats *stats) |
134 | { | 134 | { |
135 | int rc; | ||
136 | struct task_struct *tsk, *first; | 135 | struct task_struct *tsk, *first; |
136 | unsigned long flags; | ||
137 | 137 | ||
138 | /* | ||
139 | * Add additional stats from live tasks except zombie thread group | ||
140 | * leaders who are already counted with the dead tasks | ||
141 | */ | ||
138 | first = tgidtsk; | 142 | first = tgidtsk; |
139 | read_lock(&tasklist_lock); | ||
140 | if (!first) { | 143 | if (!first) { |
144 | read_lock(&tasklist_lock); | ||
141 | first = find_task_by_pid(tgid); | 145 | first = find_task_by_pid(tgid); |
142 | if (!first) { | 146 | if (!first) { |
143 | read_unlock(&tasklist_lock); | 147 | read_unlock(&tasklist_lock); |
144 | return -ESRCH; | 148 | return -ESRCH; |
145 | } | 149 | } |
146 | } | 150 | get_task_struct(first); |
151 | read_unlock(&tasklist_lock); | ||
152 | } else | ||
153 | get_task_struct(first); | ||
154 | |||
155 | /* Start with stats from dead tasks */ | ||
156 | spin_lock_irqsave(&first->signal->stats_lock, flags); | ||
157 | if (first->signal->stats) | ||
158 | memcpy(stats, first->signal->stats, sizeof(*stats)); | ||
159 | spin_unlock_irqrestore(&first->signal->stats_lock, flags); | ||
160 | |||
147 | tsk = first; | 161 | tsk = first; |
162 | read_lock(&tasklist_lock); | ||
148 | do { | 163 | do { |
164 | if (tsk->exit_state == EXIT_ZOMBIE && thread_group_leader(tsk)) | ||
165 | continue; | ||
149 | /* | 166 | /* |
150 | * Each accounting subsystem adds calls its functions to | 167 | * Accounting subsystem can call its functions here to |
151 | * fill in relevant parts of struct taskstsats as follows | 168 | * fill in relevant parts of struct taskstsats as follows |
152 | * | 169 | * |
153 | * rc = per-task-foo(stats, tsk); | 170 | * per-task-foo(stats, tsk); |
154 | * if (rc) | ||
155 | * break; | ||
156 | */ | 171 | */ |
157 | 172 | delayacct_add_tsk(stats, tsk); | |
158 | rc = delayacct_add_tsk(stats, tsk); | ||
159 | if (rc) | ||
160 | break; | ||
161 | 173 | ||
162 | } while_each_thread(first, tsk); | 174 | } while_each_thread(first, tsk); |
163 | read_unlock(&tasklist_lock); | 175 | read_unlock(&tasklist_lock); |
164 | stats->version = TASKSTATS_VERSION; | 176 | stats->version = TASKSTATS_VERSION; |
165 | 177 | ||
166 | |||
167 | /* | 178 | /* |
168 | * Accounting subsytems can also add calls here if they don't | 179 | * Accounting subsytems can also add calls here to modify |
169 | * wish to aggregate statistics for per-tgid stats | 180 | * fields of taskstats. |
170 | */ | 181 | */ |
171 | 182 | ||
172 | return rc; | 183 | return 0; |
184 | } | ||
185 | |||
186 | |||
187 | static void fill_tgid_exit(struct task_struct *tsk) | ||
188 | { | ||
189 | unsigned long flags; | ||
190 | |||
191 | spin_lock_irqsave(&tsk->signal->stats_lock, flags); | ||
192 | if (!tsk->signal->stats) | ||
193 | goto ret; | ||
194 | |||
195 | /* | ||
196 | * Each accounting subsystem calls its functions here to | ||
197 | * accumalate its per-task stats for tsk, into the per-tgid structure | ||
198 | * | ||
199 | * per-task-foo(tsk->signal->stats, tsk); | ||
200 | */ | ||
201 | delayacct_add_tsk(tsk->signal->stats, tsk); | ||
202 | ret: | ||
203 | spin_unlock_irqrestore(&tsk->signal->stats_lock, flags); | ||
204 | return; | ||
173 | } | 205 | } |
174 | 206 | ||
207 | |||
175 | static int taskstats_send_stats(struct sk_buff *skb, struct genl_info *info) | 208 | static int taskstats_send_stats(struct sk_buff *skb, struct genl_info *info) |
176 | { | 209 | { |
177 | int rc = 0; | 210 | int rc = 0; |
@@ -230,7 +263,7 @@ err: | |||
230 | 263 | ||
231 | /* Send pid data out on exit */ | 264 | /* Send pid data out on exit */ |
232 | void taskstats_exit_send(struct task_struct *tsk, struct taskstats *tidstats, | 265 | void taskstats_exit_send(struct task_struct *tsk, struct taskstats *tidstats, |
233 | struct taskstats *tgidstats) | 266 | int group_dead) |
234 | { | 267 | { |
235 | int rc; | 268 | int rc; |
236 | struct sk_buff *rep_skb; | 269 | struct sk_buff *rep_skb; |
@@ -238,13 +271,16 @@ void taskstats_exit_send(struct task_struct *tsk, struct taskstats *tidstats, | |||
238 | size_t size; | 271 | size_t size; |
239 | int is_thread_group; | 272 | int is_thread_group; |
240 | struct nlattr *na; | 273 | struct nlattr *na; |
274 | unsigned long flags; | ||
241 | 275 | ||
242 | if (!family_registered || !tidstats) | 276 | if (!family_registered || !tidstats) |
243 | return; | 277 | return; |
244 | 278 | ||
245 | is_thread_group = !thread_group_empty(tsk); | 279 | spin_lock_irqsave(&tsk->signal->stats_lock, flags); |
246 | rc = 0; | 280 | is_thread_group = tsk->signal->stats ? 1 : 0; |
281 | spin_unlock_irqrestore(&tsk->signal->stats_lock, flags); | ||
247 | 282 | ||
283 | rc = 0; | ||
248 | /* | 284 | /* |
249 | * Size includes space for nested attributes | 285 | * Size includes space for nested attributes |
250 | */ | 286 | */ |
@@ -268,30 +304,28 @@ void taskstats_exit_send(struct task_struct *tsk, struct taskstats *tidstats, | |||
268 | *tidstats); | 304 | *tidstats); |
269 | nla_nest_end(rep_skb, na); | 305 | nla_nest_end(rep_skb, na); |
270 | 306 | ||
271 | if (!is_thread_group || !tgidstats) { | 307 | if (!is_thread_group) |
272 | send_reply(rep_skb, 0, TASKSTATS_MSG_MULTICAST); | 308 | goto send; |
273 | goto ret; | ||
274 | } | ||
275 | 309 | ||
276 | rc = fill_tgid(tsk->pid, tsk, tgidstats); | ||
277 | /* | 310 | /* |
278 | * If fill_tgid() failed then one probable reason could be that the | 311 | * tsk has/had a thread group so fill the tsk->signal->stats structure |
279 | * thread group leader has exited. fill_tgid() will fail, send out | 312 | * Doesn't matter if tsk is the leader or the last group member leaving |
280 | * the pid statistics collected earlier. | ||
281 | */ | 313 | */ |
282 | if (rc < 0) { | 314 | |
283 | send_reply(rep_skb, 0, TASKSTATS_MSG_MULTICAST); | 315 | fill_tgid_exit(tsk); |
284 | goto ret; | 316 | if (!group_dead) |
285 | } | 317 | goto send; |
286 | 318 | ||
287 | na = nla_nest_start(rep_skb, TASKSTATS_TYPE_AGGR_TGID); | 319 | na = nla_nest_start(rep_skb, TASKSTATS_TYPE_AGGR_TGID); |
288 | NLA_PUT_U32(rep_skb, TASKSTATS_TYPE_TGID, (u32)tsk->tgid); | 320 | NLA_PUT_U32(rep_skb, TASKSTATS_TYPE_TGID, (u32)tsk->tgid); |
321 | /* No locking needed for tsk->signal->stats since group is dead */ | ||
289 | NLA_PUT_TYPE(rep_skb, struct taskstats, TASKSTATS_TYPE_STATS, | 322 | NLA_PUT_TYPE(rep_skb, struct taskstats, TASKSTATS_TYPE_STATS, |
290 | *tgidstats); | 323 | *tsk->signal->stats); |
291 | nla_nest_end(rep_skb, na); | 324 | nla_nest_end(rep_skb, na); |
292 | 325 | ||
326 | send: | ||
293 | send_reply(rep_skb, 0, TASKSTATS_MSG_MULTICAST); | 327 | send_reply(rep_skb, 0, TASKSTATS_MSG_MULTICAST); |
294 | goto ret; | 328 | return; |
295 | 329 | ||
296 | nla_put_failure: | 330 | nla_put_failure: |
297 | genlmsg_cancel(rep_skb, reply); | 331 | genlmsg_cancel(rep_skb, reply); |