summaryrefslogtreecommitdiffstats
path: root/kernel/taskstats.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/taskstats.c')
-rw-r--r--kernel/taskstats.c98
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,
static int fill_tgid(pid_t tgid, struct task_struct *tgidtsk,
struct taskstats *stats)
{
- int rc;
struct task_struct *tsk, *first;
+ unsigned long flags;
+ /*
+ * Add additional stats from live tasks except zombie thread group
+ * leaders who are already counted with the dead tasks
+ */
first = tgidtsk;
- read_lock(&tasklist_lock);
if (!first) {
+ read_lock(&tasklist_lock);
first = find_task_by_pid(tgid);
if (!first) {
read_unlock(&tasklist_lock);
return -ESRCH;
}
- }
+ get_task_struct(first);
+ read_unlock(&tasklist_lock);
+ } else
+ get_task_struct(first);
+
+ /* Start with stats from dead tasks */
+ spin_lock_irqsave(&first->signal->stats_lock, flags);
+ if (first->signal->stats)
+ memcpy(stats, first->signal->stats, sizeof(*stats));
+ spin_unlock_irqrestore(&first->signal->stats_lock, flags);
+
tsk = first;
+ read_lock(&tasklist_lock);
do {
+ if (tsk->exit_state == EXIT_ZOMBIE && thread_group_leader(tsk))
+ continue;
/*
- * Each accounting subsystem adds calls its functions to
+ * Accounting subsystem can call its functions here to
* fill in relevant parts of struct taskstsats as follows
*
- * rc = per-task-foo(stats, tsk);
- * if (rc)
- * break;
+ * per-task-foo(stats, tsk);
*/
-
- rc = delayacct_add_tsk(stats, tsk);
- if (rc)
- break;
+ delayacct_add_tsk(stats, tsk);
} while_each_thread(first, tsk);
read_unlock(&tasklist_lock);
stats->version = TASKSTATS_VERSION;
-
/*
- * Accounting subsytems can also add calls here if they don't
- * wish to aggregate statistics for per-tgid stats
+ * Accounting subsytems can also add calls here to modify
+ * fields of taskstats.
*/
- return rc;
+ return 0;
+}
+
+
+static void fill_tgid_exit(struct task_struct *tsk)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&tsk->signal->stats_lock, flags);
+ if (!tsk->signal->stats)
+ goto ret;
+
+ /*
+ * Each accounting subsystem calls its functions here to
+ * accumalate its per-task stats for tsk, into the per-tgid structure
+ *
+ * per-task-foo(tsk->signal->stats, tsk);
+ */
+ delayacct_add_tsk(tsk->signal->stats, tsk);
+ret:
+ spin_unlock_irqrestore(&tsk->signal->stats_lock, flags);
+ return;
}
+
static int taskstats_send_stats(struct sk_buff *skb, struct genl_info *info)
{
int rc = 0;
@@ -230,7 +263,7 @@ err:
/* Send pid data out on exit */
void taskstats_exit_send(struct task_struct *tsk, struct taskstats *tidstats,
- struct taskstats *tgidstats)
+ int group_dead)
{
int rc;
struct sk_buff *rep_skb;
@@ -238,13 +271,16 @@ void taskstats_exit_send(struct task_struct *tsk, struct taskstats *tidstats,
size_t size;
int is_thread_group;
struct nlattr *na;
+ unsigned long flags;
if (!family_registered || !tidstats)
return;
- is_thread_group = !thread_group_empty(tsk);
- rc = 0;
+ spin_lock_irqsave(&tsk->signal->stats_lock, flags);
+ is_thread_group = tsk->signal->stats ? 1 : 0;
+ spin_unlock_irqrestore(&tsk->signal->stats_lock, flags);
+ rc = 0;
/*
* Size includes space for nested attributes
*/
@@ -268,30 +304,28 @@ void taskstats_exit_send(struct task_struct *tsk, struct taskstats *tidstats,
*tidstats);
nla_nest_end(rep_skb, na);
- if (!is_thread_group || !tgidstats) {
- send_reply(rep_skb, 0, TASKSTATS_MSG_MULTICAST);
- goto ret;
- }
+ if (!is_thread_group)
+ goto send;
- rc = fill_tgid(tsk->pid, tsk, tgidstats);
/*
- * If fill_tgid() failed then one probable reason could be that the
- * thread group leader has exited. fill_tgid() will fail, send out
- * the pid statistics collected earlier.
+ * tsk has/had a thread group so fill the tsk->signal->stats structure
+ * Doesn't matter if tsk is the leader or the last group member leaving
*/
- if (rc < 0) {
- send_reply(rep_skb, 0, TASKSTATS_MSG_MULTICAST);
- goto ret;
- }
+
+ fill_tgid_exit(tsk);
+ if (!group_dead)
+ goto send;
na = nla_nest_start(rep_skb, TASKSTATS_TYPE_AGGR_TGID);
NLA_PUT_U32(rep_skb, TASKSTATS_TYPE_TGID, (u32)tsk->tgid);
+ /* No locking needed for tsk->signal->stats since group is dead */
NLA_PUT_TYPE(rep_skb, struct taskstats, TASKSTATS_TYPE_STATS,
- *tgidstats);
+ *tsk->signal->stats);
nla_nest_end(rep_skb, na);
+send:
send_reply(rep_skb, 0, TASKSTATS_MSG_MULTICAST);
- goto ret;
+ return;
nla_put_failure:
genlmsg_cancel(rep_skb, reply);