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)
This commit is contained in:
parent
5f66e7c904
commit
2fd940bf34
@ -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 void calc_uid_cputime(struct uid_entry *uid_entry,
|
||||
u64 *total_utime, u64 *total_stime)
|
||||
{
|
||||
struct user_namespace *user_ns = current_user_ns();
|
||||
struct task_struct *p, *t;
|
||||
u64 utime, stime;
|
||||
uid_t uid;
|
||||
|
||||
rcu_read_lock();
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
static int uid_cputime_show(struct seq_file *m, void *v)
|
||||
{
|
||||
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;
|
||||
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);
|
||||
}
|
||||
u64 total_utime = uid_entry->utime;
|
||||
u64 total_stime = uid_entry->stime;
|
||||
|
||||
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;
|
||||
}
|
||||
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;
|
||||
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)
|
||||
{
|
||||
struct uid_entry *uid_entry = NULL;
|
||||
struct task_struct *task, *temp;
|
||||
struct user_namespace *user_ns = current_user_ns();
|
||||
u32 bkt;
|
||||
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) {
|
||||
memset(&uid_entry->io[UID_STATE_TOTAL_CURR], 0,
|
||||
sizeof(struct io_stats));
|
||||
}
|
||||
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) {
|
||||
unlock_uid(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]);
|
||||
}
|
||||
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();
|
||||
struct task_struct *p, *t;
|
||||
struct io_stats io;
|
||||
|
||||
memset(&uid_entry->io[UID_STATE_TOTAL_CURR], 0,
|
||||
sizeof(struct io_stats));
|
||||
memset(&io, 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)
|
||||
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);
|
||||
} while_each_thread(temp, task);
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
compute_io_bucket_stats(&uid_entry->io[uid_entry->state],
|
||||
&uid_entry->io[UID_STATE_TOTAL_CURR],
|
||||
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++) {
|
||||
|
||||
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);
|
||||
update_io_stats_uid(uid_entry);
|
||||
uid_entry->state = state;
|
||||
break;
|
||||
}
|
||||
}
|
||||
unlock_uid(uid);
|
||||
|
||||
return count;
|
||||
|
Loading…
Reference in New Issue
Block a user