aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorlipeifeng <lipeifeng@oppo.com>2024-02-20 19:01:27 +0800
committerJohn Stultz <jstultz@google.com>2024-03-15 17:11:31 +0000
commitfd6ffbfe9e4c39ccf1c0b6bc25d4dd83b96cf3f4 (patch)
treefb01b3dbd5ae4c559d0dd1b4daa8f9b0e86222b9
parentae4e545e708dcd64b07c4012f14459afede95261 (diff)
downloadhikey-linaro-fd6ffbfe9e4c39ccf1c0b6bc25d4dd83b96cf3f4.tar.gz
ANDROID: uid_sys_stat: fix data-error of cputime and io
'commit b6115e14010 ("ANDROID: uid_sys_stat: split the global lock uid_lock to the fine-grained locks for each hlist in hash_table.")' The above patch split the global lock to per-uid lock to reduce lock competition. But result in data-error from uid_cputime_show and uid_io_show in some cases. E.g, if thread1 and thread2 read /proc/uid_cputime/show_uid_stat at the same time, thread2 maybe operate in partA and zero active_stime and active_utime of uid_entry when thread1 is between partB and partC, which would cause thread1 show the error data. static int uid_cputime_show(struct seq_file *m, void *v) { ... /*partA*/ for (bkt = 0, uid_entry = NULL; uid_entry == NULL && bkt < HASH_SIZE(hash_table); bkt++) { lock_uid_by_bkt(bkt); hlist_for_each_entry(uid_entry, &hash_table[bkt], hash) { uid_entry->active_stime = 0; uid_entry->active_utime = 0; } unlock_uid_by_bkt(bkt); } rcu_read_lock(); /* partB */ do_each_thread(temp, task) { ... lock_uid(uid); if (!(task->flags & PF_EXITING)) { task_cputime_adjusted(task, &utime, &stime); uid_entry->active_utime += utime; uid_entry->active_stime += stime; } unlock_uid(uid); } while_each_thread(temp, task); rcu_read_unlock(); for (bkt = 0, uid_entry = NULL; uid_entry == NULL && bkt < HASH_SIZE(hash_table); bkt++) { lock_uid_by_bkt(bkt); hlist_for_each_entry(uid_entry, &hash_table[bkt], hash) { u64 total_utime = uid_entry->utime + uid_entry->active_utime; u64 total_stime = uid_entry->stime + uid_entry->active_stime; /* partC */ seq_printf(m, "%d: %llu %llu\n", uid_entry->uid, ktime_to_us(total_utime), ktime_to_us(total_stime)); } unlock_uid_by_bkt(bkt); } The patch ensures that the calculation and seq_printf of each uid_entry is within the uid_lock range, in order to accurate data. Bug: 278138377 Change-Id: Iaa2ccd95c4b4b333f04b2ba18d7699d94017394e Signed-off-by: lipeifeng <lipeifeng@oppo.com> (cherry picked from commit ea35d2bd073214e84be242287a2e91741c6588ed)
-rw-r--r--drivers/misc/uid_sys_stats.c192
1 files changed, 60 insertions, 132 deletions
diff --git a/drivers/misc/uid_sys_stats.c b/drivers/misc/uid_sys_stats.c
index ff6bc1d6fc45..a78be7fb05ff 100644
--- a/drivers/misc/uid_sys_stats.c
+++ b/drivers/misc/uid_sys_stats.c
@@ -51,12 +51,9 @@ struct io_stats {
#define UID_STATE_FOREGROUND 0
#define UID_STATE_BACKGROUND 1
-#define UID_STATE_BUCKET_SIZE 2
-
-#define UID_STATE_TOTAL_CURR 2
-#define UID_STATE_TOTAL_LAST 3
-#define UID_STATE_DEAD_TASKS 4
-#define UID_STATE_SIZE 5
+#define UID_STATE_TOTAL_LAST 2
+#define UID_STATE_DEAD_TASKS 3
+#define UID_STATE_SIZE 4
#define MAX_TASK_COMM_LEN 256
@@ -71,8 +68,6 @@ struct uid_entry {
uid_t uid;
u64 utime;
u64 stime;
- u64 active_utime;
- u64 active_stime;
int state;
struct io_stats io[UID_STATE_SIZE];
struct hlist_node hash;
@@ -173,58 +168,47 @@ static struct uid_entry *find_or_register_uid(uid_t uid)
return uid_entry;
}
-static int uid_cputime_show(struct seq_file *m, void *v)
+static void calc_uid_cputime(struct uid_entry *uid_entry,
+ u64 *total_utime, u64 *total_stime)
{
- struct uid_entry *uid_entry = NULL;
- struct task_struct *task, *temp;
struct user_namespace *user_ns = current_user_ns();
- u64 utime;
- u64 stime;
- u32 bkt;
+ struct task_struct *p, *t;
+ u64 utime, stime;
uid_t uid;
- for (bkt = 0, uid_entry = NULL; uid_entry == NULL &&
- bkt < HASH_SIZE(hash_table); bkt++) {
- lock_uid_by_bkt(bkt);
- hlist_for_each_entry(uid_entry, &hash_table[bkt], hash) {
- uid_entry->active_stime = 0;
- uid_entry->active_utime = 0;
- }
- unlock_uid_by_bkt(bkt);
- }
-
rcu_read_lock();
- do_each_thread(temp, task) {
- uid = from_kuid_munged(user_ns, task_uid(task));
- lock_uid(uid);
-
- if (!uid_entry || uid_entry->uid != uid)
- uid_entry = find_or_register_uid(uid);
- if (!uid_entry) {
- rcu_read_unlock();
- unlock_uid(uid);
- pr_err("%s: failed to find the uid_entry for uid %d\n",
- __func__, uid);
- return -ENOMEM;
- }
- /* avoid double accounting of dying threads */
- if (!(task->flags & PF_EXITING)) {
- task_cputime_adjusted(task, &utime, &stime);
- uid_entry->active_utime += utime;
- uid_entry->active_stime += stime;
+ for_each_process(p) {
+ uid = from_kuid_munged(user_ns, task_uid(p));
+
+ if (uid != uid_entry->uid)
+ continue;
+
+ for_each_thread(p, t) {
+ /* avoid double accounting of dying threads */
+ if (!(t->flags & PF_EXITING)) {
+ task_cputime_adjusted(t, &utime, &stime);
+ *total_utime += utime;
+ *total_stime += stime;
+ }
}
- unlock_uid(uid);
- } while_each_thread(temp, task);
+ }
rcu_read_unlock();
+}
+
+static int uid_cputime_show(struct seq_file *m, void *v)
+{
+ struct uid_entry *uid_entry = NULL;
+ u32 bkt;
for (bkt = 0, uid_entry = NULL; uid_entry == NULL &&
bkt < HASH_SIZE(hash_table); bkt++) {
+
lock_uid_by_bkt(bkt);
hlist_for_each_entry(uid_entry, &hash_table[bkt], hash) {
- u64 total_utime = uid_entry->utime +
- uid_entry->active_utime;
- u64 total_stime = uid_entry->stime +
- uid_entry->active_stime;
+ u64 total_utime = uid_entry->utime;
+ u64 total_stime = uid_entry->stime;
+
+ calc_uid_cputime(uid_entry, &total_utime, &total_stime);
seq_printf(m, "%d: %llu %llu\n", uid_entry->uid,
ktime_to_us(total_utime), ktime_to_us(total_stime));
}
@@ -323,86 +307,52 @@ static void add_uid_io_stats(struct uid_entry *uid_entry,
__add_uid_io_stats(uid_entry, &task->ioac, slot);
}
-static void update_io_stats_all(void)
+static void update_io_stats_uid(struct uid_entry *uid_entry)
{
- struct uid_entry *uid_entry = NULL;
- struct task_struct *task, *temp;
struct user_namespace *user_ns = current_user_ns();
- u32 bkt;
- uid_t uid;
+ struct task_struct *p, *t;
+ struct io_stats io;
- for (bkt = 0, uid_entry = NULL; uid_entry == NULL && bkt < HASH_SIZE(hash_table);
- bkt++) {
- lock_uid_by_bkt(bkt);
- hlist_for_each_entry(uid_entry, &hash_table[bkt], hash) {
- memset(&uid_entry->io[UID_STATE_TOTAL_CURR], 0,
- sizeof(struct io_stats));
- }
- unlock_uid_by_bkt(bkt);
- }
+ memset(&io, 0, sizeof(struct io_stats));
rcu_read_lock();
- do_each_thread(temp, task) {
- uid = from_kuid_munged(user_ns, task_uid(task));
- lock_uid(uid);
- if (!uid_entry || uid_entry->uid != uid)
- uid_entry = find_or_register_uid(uid);
- if (!uid_entry) {
- unlock_uid(uid);
+ for_each_process(p) {
+ uid_t uid = from_kuid_munged(user_ns, task_uid(p));
+
+ if (uid != uid_entry->uid)
continue;
- }
- add_uid_io_stats(uid_entry, task, UID_STATE_TOTAL_CURR);
- unlock_uid(uid);
- } while_each_thread(temp, task);
- rcu_read_unlock();
- for (bkt = 0, uid_entry = NULL; uid_entry == NULL && bkt < HASH_SIZE(hash_table);
- bkt++) {
- lock_uid_by_bkt(bkt);
- hlist_for_each_entry(uid_entry, &hash_table[bkt], hash) {
- compute_io_bucket_stats(&uid_entry->io[uid_entry->state],
- &uid_entry->io[UID_STATE_TOTAL_CURR],
- &uid_entry->io[UID_STATE_TOTAL_LAST],
- &uid_entry->io[UID_STATE_DEAD_TASKS]);
+ for_each_thread(p, t) {
+ /* avoid double accounting of dying threads */
+ if (!(t->flags & PF_EXITING)) {
+ io.read_bytes += t->ioac.read_bytes;
+ io.write_bytes += compute_write_bytes(&t->ioac);
+ io.rchar += t->ioac.rchar;
+ io.wchar += t->ioac.wchar;
+ io.fsync += t->ioac.syscfs;
+ }
}
- unlock_uid_by_bkt(bkt);
}
-}
-
-static void update_io_stats_uid(struct uid_entry *uid_entry)
-{
- struct task_struct *task, *temp;
- struct user_namespace *user_ns = current_user_ns();
-
- memset(&uid_entry->io[UID_STATE_TOTAL_CURR], 0,
- sizeof(struct io_stats));
-
- rcu_read_lock();
- do_each_thread(temp, task) {
- if (from_kuid_munged(user_ns, task_uid(task)) != uid_entry->uid)
- continue;
- add_uid_io_stats(uid_entry, task, UID_STATE_TOTAL_CURR);
- } while_each_thread(temp, task);
rcu_read_unlock();
- compute_io_bucket_stats(&uid_entry->io[uid_entry->state],
- &uid_entry->io[UID_STATE_TOTAL_CURR],
- &uid_entry->io[UID_STATE_TOTAL_LAST],
- &uid_entry->io[UID_STATE_DEAD_TASKS]);
+ compute_io_bucket_stats(&uid_entry->io[uid_entry->state], &io,
+ &uid_entry->io[UID_STATE_TOTAL_LAST],
+ &uid_entry->io[UID_STATE_DEAD_TASKS]);
}
-
static int uid_io_show(struct seq_file *m, void *v)
{
- struct uid_entry *uid_entry;
+
+ struct uid_entry *uid_entry = NULL;
u32 bkt;
- update_io_stats_all();
for (bkt = 0, uid_entry = NULL; uid_entry == NULL && bkt < HASH_SIZE(hash_table);
- bkt++) {
-
+ bkt++) {
lock_uid_by_bkt(bkt);
hlist_for_each_entry(uid_entry, &hash_table[bkt], hash) {
+
+ update_io_stats_uid(uid_entry);
+
seq_printf(m, "%d %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu\n",
uid_entry->uid,
uid_entry->io[UID_STATE_FOREGROUND].rchar,
@@ -446,7 +396,6 @@ static ssize_t uid_procstat_write(struct file *file,
uid_t uid;
int argc, state;
char input[128];
- struct uid_entry uid_entry_tmp;
if (count >= sizeof(input))
return -EINVAL;
@@ -475,29 +424,8 @@ static ssize_t uid_procstat_write(struct file *file,
return count;
}
- /*
- * Update_io_stats_uid_locked would take a long lock-time of uid_lock
- * due to call do_each_thread to compute uid_entry->io, which would
- * cause to lock competition sometime.
- *
- * Using uid_entry_tmp to get the result of Update_io_stats_uid,
- * so that we can unlock_uid during update_io_stats_uid, in order
- * to avoid the unnecessary lock-time of uid_lock.
- */
- uid_entry_tmp = *uid_entry;
-
- unlock_uid(uid);
- update_io_stats_uid(&uid_entry_tmp);
-
- lock_uid(uid);
- hlist_for_each_entry(uid_entry, &hash_table[hash_min(uid, HASH_BITS(hash_table))], hash) {
- if (uid_entry->uid == uid_entry_tmp.uid) {
- memcpy(uid_entry->io, uid_entry_tmp.io,
- sizeof(struct io_stats) * UID_STATE_SIZE);
- uid_entry->state = state;
- break;
- }
- }
+ update_io_stats_uid(uid_entry);
+ uid_entry->state = state;
unlock_uid(uid);
return count;