ANDROID: uid_sys_stat: split the global lock uid_lock to the fine-grained
locks for each hlist in hash_table. 1.Hash_table in uid_sys_stat is protected by a global lock named id_lock, which causes some lock competition issue. Actually, uid_lock can be split to several file-grained locks for each hlist in hash_table, which avoid the unnecessary lock competition when get different-uid process info. 2. Switching rt-mutex to spinlock, in order to operate with read_rcu_lock. Bug: 278138377 Signed-off-by: Peifeng Li <lipeifeng@oppo.com> (cherry picked from https://android-review.googlesource.com/q/commit:c949fbdce0bc792dea206c709d909094be579c3a) Merged-In: Ib252b65e9aebe3a594e6edf075f7aa01f8e6105d Change-Id: Ib252b65e9aebe3a594e6edf075f7aa01f8e6105d
This commit is contained in:
parent
9290fc3e8d
commit
97f2f8a065
1 changed files with 115 additions and 53 deletions
|
|
@ -23,17 +23,20 @@
|
||||||
#include <linux/mm.h>
|
#include <linux/mm.h>
|
||||||
#include <linux/proc_fs.h>
|
#include <linux/proc_fs.h>
|
||||||
#include <linux/profile.h>
|
#include <linux/profile.h>
|
||||||
#include <linux/rtmutex.h>
|
|
||||||
#include <linux/sched/cputime.h>
|
#include <linux/sched/cputime.h>
|
||||||
#include <linux/seq_file.h>
|
#include <linux/seq_file.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/uaccess.h>
|
#include <linux/uaccess.h>
|
||||||
|
#include <linux/spinlock_types.h>
|
||||||
|
|
||||||
#define UID_HASH_BITS 10
|
#define UID_HASH_BITS 10
|
||||||
|
#define UID_HASH_NUMS (1 << UID_HASH_BITS)
|
||||||
DECLARE_HASHTABLE(hash_table, UID_HASH_BITS);
|
DECLARE_HASHTABLE(hash_table, UID_HASH_BITS);
|
||||||
|
/*
|
||||||
|
* uid_lock[bkt] ensure consistency of hash_table[bkt]
|
||||||
|
*/
|
||||||
|
spinlock_t uid_lock[UID_HASH_NUMS];
|
||||||
|
|
||||||
static DEFINE_RT_MUTEX(uid_lock);
|
|
||||||
static struct proc_dir_entry *cpu_parent;
|
static struct proc_dir_entry *cpu_parent;
|
||||||
static struct proc_dir_entry *io_parent;
|
static struct proc_dir_entry *io_parent;
|
||||||
static struct proc_dir_entry *proc_parent;
|
static struct proc_dir_entry *proc_parent;
|
||||||
|
|
@ -78,6 +81,32 @@ struct uid_entry {
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static inline int trylock_uid(uid_t uid)
|
||||||
|
{
|
||||||
|
return spin_trylock(
|
||||||
|
&uid_lock[hash_min(uid, HASH_BITS(hash_table))]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void lock_uid(uid_t uid)
|
||||||
|
{
|
||||||
|
spin_lock(&uid_lock[hash_min(uid, HASH_BITS(hash_table))]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void unlock_uid(uid_t uid)
|
||||||
|
{
|
||||||
|
spin_unlock(&uid_lock[hash_min(uid, HASH_BITS(hash_table))]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void lock_uid_by_bkt(u32 bkt)
|
||||||
|
{
|
||||||
|
spin_lock(&uid_lock[bkt]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void unlock_uid_by_bkt(u32 bkt)
|
||||||
|
{
|
||||||
|
spin_unlock(&uid_lock[bkt]);
|
||||||
|
}
|
||||||
|
|
||||||
static u64 compute_write_bytes(struct task_io_accounting *ioac)
|
static u64 compute_write_bytes(struct task_io_accounting *ioac)
|
||||||
{
|
{
|
||||||
if (ioac->write_bytes <= ioac->cancelled_write_bytes)
|
if (ioac->write_bytes <= ioac->cancelled_write_bytes)
|
||||||
|
|
@ -333,24 +362,29 @@ static int uid_cputime_show(struct seq_file *m, void *v)
|
||||||
struct user_namespace *user_ns = current_user_ns();
|
struct user_namespace *user_ns = current_user_ns();
|
||||||
u64 utime;
|
u64 utime;
|
||||||
u64 stime;
|
u64 stime;
|
||||||
unsigned long bkt;
|
u32 bkt;
|
||||||
uid_t uid;
|
uid_t uid;
|
||||||
|
|
||||||
rt_mutex_lock(&uid_lock);
|
for (bkt = 0, uid_entry = NULL; uid_entry == NULL &&
|
||||||
|
bkt < HASH_SIZE(hash_table); bkt++) {
|
||||||
hash_for_each(hash_table, bkt, uid_entry, hash) {
|
lock_uid_by_bkt(bkt);
|
||||||
uid_entry->active_stime = 0;
|
hlist_for_each_entry(uid_entry, &hash_table[bkt], hash) {
|
||||||
uid_entry->active_utime = 0;
|
uid_entry->active_stime = 0;
|
||||||
|
uid_entry->active_utime = 0;
|
||||||
|
}
|
||||||
|
unlock_uid_by_bkt(bkt);
|
||||||
}
|
}
|
||||||
|
|
||||||
rcu_read_lock();
|
rcu_read_lock();
|
||||||
do_each_thread(temp, task) {
|
do_each_thread(temp, task) {
|
||||||
uid = from_kuid_munged(user_ns, task_uid(task));
|
uid = from_kuid_munged(user_ns, task_uid(task));
|
||||||
|
lock_uid(uid);
|
||||||
|
|
||||||
if (!uid_entry || uid_entry->uid != uid)
|
if (!uid_entry || uid_entry->uid != uid)
|
||||||
uid_entry = find_or_register_uid(uid);
|
uid_entry = find_or_register_uid(uid);
|
||||||
if (!uid_entry) {
|
if (!uid_entry) {
|
||||||
rcu_read_unlock();
|
rcu_read_unlock();
|
||||||
rt_mutex_unlock(&uid_lock);
|
unlock_uid(uid);
|
||||||
pr_err("%s: failed to find the uid_entry for uid %d\n",
|
pr_err("%s: failed to find the uid_entry for uid %d\n",
|
||||||
__func__, uid);
|
__func__, uid);
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
@ -361,19 +395,24 @@ static int uid_cputime_show(struct seq_file *m, void *v)
|
||||||
uid_entry->active_utime += utime;
|
uid_entry->active_utime += utime;
|
||||||
uid_entry->active_stime += stime;
|
uid_entry->active_stime += stime;
|
||||||
}
|
}
|
||||||
|
unlock_uid(uid);
|
||||||
} while_each_thread(temp, task);
|
} while_each_thread(temp, task);
|
||||||
rcu_read_unlock();
|
rcu_read_unlock();
|
||||||
|
|
||||||
hash_for_each(hash_table, bkt, uid_entry, hash) {
|
for (bkt = 0, uid_entry = NULL; uid_entry == NULL &&
|
||||||
u64 total_utime = uid_entry->utime +
|
bkt < HASH_SIZE(hash_table); bkt++) {
|
||||||
uid_entry->active_utime;
|
lock_uid_by_bkt(bkt);
|
||||||
u64 total_stime = uid_entry->stime +
|
hlist_for_each_entry(uid_entry, &hash_table[bkt], hash) {
|
||||||
uid_entry->active_stime;
|
u64 total_utime = uid_entry->utime +
|
||||||
seq_printf(m, "%d: %llu %llu\n", uid_entry->uid,
|
uid_entry->active_utime;
|
||||||
ktime_to_us(total_utime), ktime_to_us(total_stime));
|
u64 total_stime = uid_entry->stime +
|
||||||
|
uid_entry->active_stime;
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
rt_mutex_unlock(&uid_lock);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -421,9 +460,8 @@ static ssize_t uid_remove_write(struct file *file,
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
rt_mutex_lock(&uid_lock);
|
|
||||||
|
|
||||||
for (; uid_start <= uid_end; uid_start++) {
|
for (; uid_start <= uid_end; uid_start++) {
|
||||||
|
lock_uid(uid_start);
|
||||||
hash_for_each_possible_safe(hash_table, uid_entry, tmp,
|
hash_for_each_possible_safe(hash_table, uid_entry, tmp,
|
||||||
hash, (uid_t)uid_start) {
|
hash, (uid_t)uid_start) {
|
||||||
if (uid_start == uid_entry->uid) {
|
if (uid_start == uid_entry->uid) {
|
||||||
|
|
@ -432,9 +470,9 @@ static ssize_t uid_remove_write(struct file *file,
|
||||||
kfree(uid_entry);
|
kfree(uid_entry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
unlock_uid(uid_start);
|
||||||
}
|
}
|
||||||
|
|
||||||
rt_mutex_unlock(&uid_lock);
|
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -472,37 +510,51 @@ static void add_uid_io_stats(struct uid_entry *uid_entry,
|
||||||
__add_uid_io_stats(uid_entry, &task->ioac, slot);
|
__add_uid_io_stats(uid_entry, &task->ioac, slot);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void update_io_stats_all_locked(void)
|
static void update_io_stats_all(void)
|
||||||
{
|
{
|
||||||
struct uid_entry *uid_entry = NULL;
|
struct uid_entry *uid_entry = NULL;
|
||||||
struct task_struct *task, *temp;
|
struct task_struct *task, *temp;
|
||||||
struct user_namespace *user_ns = current_user_ns();
|
struct user_namespace *user_ns = current_user_ns();
|
||||||
unsigned long bkt;
|
u32 bkt;
|
||||||
uid_t uid;
|
uid_t uid;
|
||||||
|
|
||||||
hash_for_each(hash_table, bkt, uid_entry, hash) {
|
for (bkt = 0, uid_entry = NULL; uid_entry == NULL && bkt < HASH_SIZE(hash_table);
|
||||||
memset(&uid_entry->io[UID_STATE_TOTAL_CURR], 0,
|
bkt++) {
|
||||||
sizeof(struct io_stats));
|
lock_uid_by_bkt(bkt);
|
||||||
set_io_uid_tasks_zero(uid_entry);
|
hlist_for_each_entry(uid_entry, &hash_table[bkt], hash) {
|
||||||
|
memset(&uid_entry->io[UID_STATE_TOTAL_CURR], 0,
|
||||||
|
sizeof(struct io_stats));
|
||||||
|
set_io_uid_tasks_zero(uid_entry);
|
||||||
|
}
|
||||||
|
unlock_uid_by_bkt(bkt);
|
||||||
}
|
}
|
||||||
|
|
||||||
rcu_read_lock();
|
rcu_read_lock();
|
||||||
do_each_thread(temp, task) {
|
do_each_thread(temp, task) {
|
||||||
uid = from_kuid_munged(user_ns, task_uid(task));
|
uid = from_kuid_munged(user_ns, task_uid(task));
|
||||||
|
lock_uid(uid);
|
||||||
if (!uid_entry || uid_entry->uid != uid)
|
if (!uid_entry || uid_entry->uid != uid)
|
||||||
uid_entry = find_or_register_uid(uid);
|
uid_entry = find_or_register_uid(uid);
|
||||||
if (!uid_entry)
|
if (!uid_entry) {
|
||||||
|
unlock_uid(uid);
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
add_uid_io_stats(uid_entry, task, UID_STATE_TOTAL_CURR);
|
add_uid_io_stats(uid_entry, task, UID_STATE_TOTAL_CURR);
|
||||||
|
unlock_uid(uid);
|
||||||
} while_each_thread(temp, task);
|
} while_each_thread(temp, task);
|
||||||
rcu_read_unlock();
|
rcu_read_unlock();
|
||||||
|
|
||||||
hash_for_each(hash_table, bkt, uid_entry, hash) {
|
for (bkt = 0, uid_entry = NULL; uid_entry == NULL && bkt < HASH_SIZE(hash_table);
|
||||||
compute_io_bucket_stats(&uid_entry->io[uid_entry->state],
|
bkt++) {
|
||||||
&uid_entry->io[UID_STATE_TOTAL_CURR],
|
lock_uid_by_bkt(bkt);
|
||||||
&uid_entry->io[UID_STATE_TOTAL_LAST],
|
hlist_for_each_entry(uid_entry, &hash_table[bkt], hash) {
|
||||||
&uid_entry->io[UID_STATE_DEAD_TASKS]);
|
compute_io_bucket_stats(&uid_entry->io[uid_entry->state],
|
||||||
compute_io_uid_tasks(uid_entry);
|
&uid_entry->io[UID_STATE_TOTAL_CURR],
|
||||||
|
&uid_entry->io[UID_STATE_TOTAL_LAST],
|
||||||
|
&uid_entry->io[UID_STATE_DEAD_TASKS]);
|
||||||
|
compute_io_uid_tasks(uid_entry);
|
||||||
|
}
|
||||||
|
unlock_uid_by_bkt(bkt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -534,14 +586,15 @@ static void update_io_stats_uid_locked(struct uid_entry *uid_entry)
|
||||||
static int uid_io_show(struct seq_file *m, void *v)
|
static int uid_io_show(struct seq_file *m, void *v)
|
||||||
{
|
{
|
||||||
struct uid_entry *uid_entry;
|
struct uid_entry *uid_entry;
|
||||||
unsigned long bkt;
|
u32 bkt;
|
||||||
|
|
||||||
rt_mutex_lock(&uid_lock);
|
update_io_stats_all();
|
||||||
|
for (bkt = 0, uid_entry = NULL; uid_entry == NULL && bkt < HASH_SIZE(hash_table);
|
||||||
|
bkt++) {
|
||||||
|
|
||||||
update_io_stats_all_locked();
|
lock_uid_by_bkt(bkt);
|
||||||
|
hlist_for_each_entry(uid_entry, &hash_table[bkt], hash) {
|
||||||
hash_for_each(hash_table, bkt, uid_entry, hash) {
|
seq_printf(m, "%d %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu\n",
|
||||||
seq_printf(m, "%d %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu\n",
|
|
||||||
uid_entry->uid,
|
uid_entry->uid,
|
||||||
uid_entry->io[UID_STATE_FOREGROUND].rchar,
|
uid_entry->io[UID_STATE_FOREGROUND].rchar,
|
||||||
uid_entry->io[UID_STATE_FOREGROUND].wchar,
|
uid_entry->io[UID_STATE_FOREGROUND].wchar,
|
||||||
|
|
@ -554,10 +607,11 @@ static int uid_io_show(struct seq_file *m, void *v)
|
||||||
uid_entry->io[UID_STATE_FOREGROUND].fsync,
|
uid_entry->io[UID_STATE_FOREGROUND].fsync,
|
||||||
uid_entry->io[UID_STATE_BACKGROUND].fsync);
|
uid_entry->io[UID_STATE_BACKGROUND].fsync);
|
||||||
|
|
||||||
show_io_uid_tasks(m, uid_entry);
|
show_io_uid_tasks(m, uid_entry);
|
||||||
|
}
|
||||||
|
unlock_uid_by_bkt(bkt);
|
||||||
}
|
}
|
||||||
|
|
||||||
rt_mutex_unlock(&uid_lock);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -601,16 +655,15 @@ static ssize_t uid_procstat_write(struct file *file,
|
||||||
if (state != UID_STATE_BACKGROUND && state != UID_STATE_FOREGROUND)
|
if (state != UID_STATE_BACKGROUND && state != UID_STATE_FOREGROUND)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
rt_mutex_lock(&uid_lock);
|
lock_uid(uid);
|
||||||
|
|
||||||
uid_entry = find_or_register_uid(uid);
|
uid_entry = find_or_register_uid(uid);
|
||||||
if (!uid_entry) {
|
if (!uid_entry) {
|
||||||
rt_mutex_unlock(&uid_lock);
|
unlock_uid(uid);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (uid_entry->state == state) {
|
if (uid_entry->state == state) {
|
||||||
rt_mutex_unlock(&uid_lock);
|
unlock_uid(uid);
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -618,7 +671,7 @@ static ssize_t uid_procstat_write(struct file *file,
|
||||||
|
|
||||||
uid_entry->state = state;
|
uid_entry->state = state;
|
||||||
|
|
||||||
rt_mutex_unlock(&uid_lock);
|
unlock_uid(uid);
|
||||||
|
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
@ -649,10 +702,9 @@ static void update_stats_workfn(struct work_struct *work)
|
||||||
struct task_entry *task_entry __maybe_unused;
|
struct task_entry *task_entry __maybe_unused;
|
||||||
struct llist_node *node;
|
struct llist_node *node;
|
||||||
|
|
||||||
rt_mutex_lock(&uid_lock);
|
|
||||||
|
|
||||||
node = llist_del_all(&work_usw);
|
node = llist_del_all(&work_usw);
|
||||||
llist_for_each_entry_safe(usw, t, node, node) {
|
llist_for_each_entry_safe(usw, t, node, node) {
|
||||||
|
lock_uid(usw->uid);
|
||||||
uid_entry = find_uid_entry(usw->uid);
|
uid_entry = find_uid_entry(usw->uid);
|
||||||
if (!uid_entry)
|
if (!uid_entry)
|
||||||
goto next;
|
goto next;
|
||||||
|
|
@ -669,12 +721,13 @@ static void update_stats_workfn(struct work_struct *work)
|
||||||
#endif
|
#endif
|
||||||
__add_uid_io_stats(uid_entry, &usw->ioac, UID_STATE_DEAD_TASKS);
|
__add_uid_io_stats(uid_entry, &usw->ioac, UID_STATE_DEAD_TASKS);
|
||||||
next:
|
next:
|
||||||
|
unlock_uid(usw->uid);
|
||||||
#ifdef CONFIG_UID_SYS_STATS_DEBUG
|
#ifdef CONFIG_UID_SYS_STATS_DEBUG
|
||||||
put_task_struct(usw->task);
|
put_task_struct(usw->task);
|
||||||
#endif
|
#endif
|
||||||
kfree(usw);
|
kfree(usw);
|
||||||
}
|
}
|
||||||
rt_mutex_unlock(&uid_lock);
|
|
||||||
}
|
}
|
||||||
static DECLARE_WORK(update_stats_work, update_stats_workfn);
|
static DECLARE_WORK(update_stats_work, update_stats_workfn);
|
||||||
|
|
||||||
|
|
@ -690,7 +743,7 @@ static int process_notifier(struct notifier_block *self,
|
||||||
return NOTIFY_OK;
|
return NOTIFY_OK;
|
||||||
|
|
||||||
uid = from_kuid_munged(current_user_ns(), task_uid(task));
|
uid = from_kuid_munged(current_user_ns(), task_uid(task));
|
||||||
if (!rt_mutex_trylock(&uid_lock)) {
|
if (!trylock_uid(uid)) {
|
||||||
struct update_stats_work *usw;
|
struct update_stats_work *usw;
|
||||||
|
|
||||||
usw = kmalloc(sizeof(struct update_stats_work), GFP_KERNEL);
|
usw = kmalloc(sizeof(struct update_stats_work), GFP_KERNEL);
|
||||||
|
|
@ -724,7 +777,7 @@ static int process_notifier(struct notifier_block *self,
|
||||||
add_uid_io_stats(uid_entry, task, UID_STATE_DEAD_TASKS);
|
add_uid_io_stats(uid_entry, task, UID_STATE_DEAD_TASKS);
|
||||||
|
|
||||||
exit:
|
exit:
|
||||||
rt_mutex_unlock(&uid_lock);
|
unlock_uid(uid);
|
||||||
return NOTIFY_OK;
|
return NOTIFY_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -732,9 +785,18 @@ static struct notifier_block process_notifier_block = {
|
||||||
.notifier_call = process_notifier,
|
.notifier_call = process_notifier,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static void init_hash_table_and_lock(void)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
hash_init(hash_table);
|
||||||
|
for (i = 0; i < UID_HASH_NUMS; i++)
|
||||||
|
spin_lock_init(&uid_lock[i]);
|
||||||
|
}
|
||||||
|
|
||||||
static int __init proc_uid_sys_stats_init(void)
|
static int __init proc_uid_sys_stats_init(void)
|
||||||
{
|
{
|
||||||
hash_init(hash_table);
|
init_hash_table_and_lock();
|
||||||
|
|
||||||
cpu_parent = proc_mkdir("uid_cputime", NULL);
|
cpu_parent = proc_mkdir("uid_cputime", NULL);
|
||||||
if (!cpu_parent) {
|
if (!cpu_parent) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue