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); |
