mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-07 19:30:30 +09:00
ANDROID: uid_cputime: add per-uid IO usage accounting
IO usages are accounted in foreground and background buckets. For each uid, io usage is calculated in two steps. delta = current total of all uid tasks - previus total current bucket += delta Bucket is determined by current uid stat. Userspace writes to /proc/uid_procstat/set <uid> <stat> when uid stat is updated. /proc/uid_io/stats shows IO usage in this format. <uid> <foreground IO> <background IO> Bug: 34198239 Bug: 120442023 Change-Id: Ib8bebda53e7a56f45ea3eb0ec9a3153d44188102 Signed-off-by: Jin Qian <jinqian@google.com> [connoro: Used by storaged.] [astrachan: Note: this version does not track fsync syscalls; this is implemented in a follow up due to the fact it depends on modifications to the task_xacct feature.] [astrachan: Folded in the following changes: e003a91d8f42 ("ANDROID: uid_sys_stats: allow writing same state") 89b984bf2efd ("ANDROID: uid_sys_stats: fix negative write bytes.") bb5ee21cc4dd ("ANDROID: uid_sys_stats: change to use rt_mutex") 9297d5a160c7 ("ANDROID: uid_sys_stats: reduce update_io_stats overhead") 89402d07fe91 ("ANDROID: uid_sys_stats: defer io stats calulation for dead tasks") 6dc5d8173a8c ("ANDROID: uid_sys_stats: check previous uid_entry before call find_or_register_uid") 0ca2ece8f7ec ("ANDROID: uid_sys_stats: log task io with a debug flag") c9c096ef0e67 ("ANDROID: uid_sys_stats: Copy task_struct comm field to bigger buffer")] Signed-off-by: Alistair Strachan <astrachan@google.com>
This commit is contained in:
@@ -515,9 +515,18 @@ config MISC_RTSX
|
||||
|
||||
config UID_SYS_STATS
|
||||
bool "Per-UID statistics"
|
||||
depends on PROFILING
|
||||
depends on PROFILING && TASK_IO_ACCOUNTING
|
||||
help
|
||||
Per UID based cpu time statistics exported to /proc/uid_cputime
|
||||
Per UID based io statistics exported to /proc/uid_io
|
||||
Per UID based procstat control in /proc/uid_procstat
|
||||
|
||||
config UID_SYS_STATS_DEBUG
|
||||
bool "Per-TASK statistics"
|
||||
depends on UID_SYS_STATS
|
||||
default n
|
||||
help
|
||||
Per TASK based io statistics exported to /proc/uid_io
|
||||
|
||||
source "drivers/misc/c2port/Kconfig"
|
||||
source "drivers/misc/eeprom/Kconfig"
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/profile.h>
|
||||
#include <linux/rtmutex.h>
|
||||
@@ -32,7 +33,35 @@
|
||||
DECLARE_HASHTABLE(hash_table, UID_HASH_BITS);
|
||||
|
||||
static DEFINE_RT_MUTEX(uid_lock);
|
||||
static struct proc_dir_entry *parent;
|
||||
static struct proc_dir_entry *cpu_parent;
|
||||
static struct proc_dir_entry *io_parent;
|
||||
static struct proc_dir_entry *proc_parent;
|
||||
|
||||
struct io_stats {
|
||||
u64 read_bytes;
|
||||
u64 write_bytes;
|
||||
u64 rchar;
|
||||
u64 wchar;
|
||||
u64 fsync;
|
||||
};
|
||||
|
||||
#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 MAX_TASK_COMM_LEN 256
|
||||
|
||||
struct task_entry {
|
||||
char comm[MAX_TASK_COMM_LEN];
|
||||
pid_t pid;
|
||||
struct io_stats io[UID_STATE_SIZE];
|
||||
struct hlist_node hash;
|
||||
};
|
||||
|
||||
struct uid_entry {
|
||||
uid_t uid;
|
||||
@@ -40,9 +69,233 @@ struct uid_entry {
|
||||
u64 stime;
|
||||
u64 active_utime;
|
||||
u64 active_stime;
|
||||
int state;
|
||||
struct io_stats io[UID_STATE_SIZE];
|
||||
struct hlist_node hash;
|
||||
#ifdef CONFIG_UID_SYS_STATS_DEBUG
|
||||
DECLARE_HASHTABLE(task_entries, UID_HASH_BITS);
|
||||
#endif
|
||||
};
|
||||
|
||||
static u64 compute_write_bytes(struct task_struct *task)
|
||||
{
|
||||
if (task->ioac.write_bytes <= task->ioac.cancelled_write_bytes)
|
||||
return 0;
|
||||
|
||||
return task->ioac.write_bytes - task->ioac.cancelled_write_bytes;
|
||||
}
|
||||
|
||||
static void compute_io_bucket_stats(struct io_stats *io_bucket,
|
||||
struct io_stats *io_curr,
|
||||
struct io_stats *io_last,
|
||||
struct io_stats *io_dead)
|
||||
{
|
||||
/* tasks could switch to another uid group, but its io_last in the
|
||||
* previous uid group could still be positive.
|
||||
* therefore before each update, do an overflow check first
|
||||
*/
|
||||
int64_t delta;
|
||||
|
||||
delta = io_curr->read_bytes + io_dead->read_bytes -
|
||||
io_last->read_bytes;
|
||||
io_bucket->read_bytes += delta > 0 ? delta : 0;
|
||||
delta = io_curr->write_bytes + io_dead->write_bytes -
|
||||
io_last->write_bytes;
|
||||
io_bucket->write_bytes += delta > 0 ? delta : 0;
|
||||
delta = io_curr->rchar + io_dead->rchar - io_last->rchar;
|
||||
io_bucket->rchar += delta > 0 ? delta : 0;
|
||||
delta = io_curr->wchar + io_dead->wchar - io_last->wchar;
|
||||
io_bucket->wchar += delta > 0 ? delta : 0;
|
||||
delta = io_curr->fsync + io_dead->fsync - io_last->fsync;
|
||||
io_bucket->fsync += delta > 0 ? delta : 0;
|
||||
|
||||
io_last->read_bytes = io_curr->read_bytes;
|
||||
io_last->write_bytes = io_curr->write_bytes;
|
||||
io_last->rchar = io_curr->rchar;
|
||||
io_last->wchar = io_curr->wchar;
|
||||
io_last->fsync = io_curr->fsync;
|
||||
|
||||
memset(io_dead, 0, sizeof(struct io_stats));
|
||||
}
|
||||
|
||||
#ifdef CONFIG_UID_SYS_STATS_DEBUG
|
||||
static void get_full_task_comm(struct task_entry *task_entry,
|
||||
struct task_struct *task)
|
||||
{
|
||||
int i = 0, offset = 0, len = 0;
|
||||
/* save one byte for terminating null character */
|
||||
int unused_len = MAX_TASK_COMM_LEN - TASK_COMM_LEN - 1;
|
||||
char buf[unused_len];
|
||||
struct mm_struct *mm = task->mm;
|
||||
|
||||
/* fill the first TASK_COMM_LEN bytes with thread name */
|
||||
__get_task_comm(task_entry->comm, TASK_COMM_LEN, task);
|
||||
i = strlen(task_entry->comm);
|
||||
while (i < TASK_COMM_LEN)
|
||||
task_entry->comm[i++] = ' ';
|
||||
|
||||
/* next the executable file name */
|
||||
if (mm) {
|
||||
down_read(&mm->mmap_sem);
|
||||
if (mm->exe_file) {
|
||||
char *pathname = d_path(&mm->exe_file->f_path, buf,
|
||||
unused_len);
|
||||
|
||||
if (!IS_ERR(pathname)) {
|
||||
len = strlcpy(task_entry->comm + i, pathname,
|
||||
unused_len);
|
||||
i += len;
|
||||
task_entry->comm[i++] = ' ';
|
||||
unused_len--;
|
||||
}
|
||||
}
|
||||
up_read(&mm->mmap_sem);
|
||||
}
|
||||
unused_len -= len;
|
||||
|
||||
/* fill the rest with command line argument
|
||||
* replace each null or new line character
|
||||
* between args in argv with whitespace */
|
||||
len = get_cmdline(task, buf, unused_len);
|
||||
while (offset < len) {
|
||||
if (buf[offset] != '\0' && buf[offset] != '\n')
|
||||
task_entry->comm[i++] = buf[offset];
|
||||
else
|
||||
task_entry->comm[i++] = ' ';
|
||||
offset++;
|
||||
}
|
||||
|
||||
/* get rid of trailing whitespaces in case when arg is memset to
|
||||
* zero before being reset in userspace
|
||||
*/
|
||||
while (task_entry->comm[i-1] == ' ')
|
||||
i--;
|
||||
task_entry->comm[i] = '\0';
|
||||
}
|
||||
|
||||
static struct task_entry *find_task_entry(struct uid_entry *uid_entry,
|
||||
struct task_struct *task)
|
||||
{
|
||||
struct task_entry *task_entry;
|
||||
|
||||
hash_for_each_possible(uid_entry->task_entries, task_entry, hash,
|
||||
task->pid) {
|
||||
if (task->pid == task_entry->pid) {
|
||||
/* if thread name changed, update the entire command */
|
||||
int len = strnchr(task_entry->comm, ' ', TASK_COMM_LEN)
|
||||
- task_entry->comm;
|
||||
|
||||
if (strncmp(task_entry->comm, task->comm, len))
|
||||
get_full_task_comm(task_entry, task);
|
||||
return task_entry;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct task_entry *find_or_register_task(struct uid_entry *uid_entry,
|
||||
struct task_struct *task)
|
||||
{
|
||||
struct task_entry *task_entry;
|
||||
pid_t pid = task->pid;
|
||||
|
||||
task_entry = find_task_entry(uid_entry, task);
|
||||
if (task_entry)
|
||||
return task_entry;
|
||||
|
||||
task_entry = kzalloc(sizeof(struct task_entry), GFP_ATOMIC);
|
||||
if (!task_entry)
|
||||
return NULL;
|
||||
|
||||
get_full_task_comm(task_entry, task);
|
||||
|
||||
task_entry->pid = pid;
|
||||
hash_add(uid_entry->task_entries, &task_entry->hash, (unsigned int)pid);
|
||||
|
||||
return task_entry;
|
||||
}
|
||||
|
||||
static void remove_uid_tasks(struct uid_entry *uid_entry)
|
||||
{
|
||||
struct task_entry *task_entry;
|
||||
unsigned long bkt_task;
|
||||
struct hlist_node *tmp_task;
|
||||
|
||||
hash_for_each_safe(uid_entry->task_entries, bkt_task,
|
||||
tmp_task, task_entry, hash) {
|
||||
hash_del(&task_entry->hash);
|
||||
kfree(task_entry);
|
||||
}
|
||||
}
|
||||
|
||||
static void set_io_uid_tasks_zero(struct uid_entry *uid_entry)
|
||||
{
|
||||
struct task_entry *task_entry;
|
||||
unsigned long bkt_task;
|
||||
|
||||
hash_for_each(uid_entry->task_entries, bkt_task, task_entry, hash) {
|
||||
memset(&task_entry->io[UID_STATE_TOTAL_CURR], 0,
|
||||
sizeof(struct io_stats));
|
||||
}
|
||||
}
|
||||
|
||||
static void add_uid_tasks_io_stats(struct uid_entry *uid_entry,
|
||||
struct task_struct *task, int slot)
|
||||
{
|
||||
struct task_entry *task_entry = find_or_register_task(uid_entry, task);
|
||||
struct io_stats *task_io_slot = &task_entry->io[slot];
|
||||
|
||||
task_io_slot->read_bytes += task->ioac.read_bytes;
|
||||
task_io_slot->write_bytes += compute_write_bytes(task);
|
||||
task_io_slot->rchar += task->ioac.rchar;
|
||||
task_io_slot->wchar += task->ioac.wchar;
|
||||
}
|
||||
|
||||
static void compute_io_uid_tasks(struct uid_entry *uid_entry)
|
||||
{
|
||||
struct task_entry *task_entry;
|
||||
unsigned long bkt_task;
|
||||
|
||||
hash_for_each(uid_entry->task_entries, bkt_task, task_entry, hash) {
|
||||
compute_io_bucket_stats(&task_entry->io[uid_entry->state],
|
||||
&task_entry->io[UID_STATE_TOTAL_CURR],
|
||||
&task_entry->io[UID_STATE_TOTAL_LAST],
|
||||
&task_entry->io[UID_STATE_DEAD_TASKS]);
|
||||
}
|
||||
}
|
||||
|
||||
static void show_io_uid_tasks(struct seq_file *m, struct uid_entry *uid_entry)
|
||||
{
|
||||
struct task_entry *task_entry;
|
||||
unsigned long bkt_task;
|
||||
|
||||
hash_for_each(uid_entry->task_entries, bkt_task, task_entry, hash) {
|
||||
/* Separated by comma because space exists in task comm */
|
||||
seq_printf(m, "task,%s,%lu,%llu,%llu,%llu,%llu,%llu,%llu,%llu,%llu,%llu,%llu\n",
|
||||
task_entry->comm,
|
||||
(unsigned long)task_entry->pid,
|
||||
task_entry->io[UID_STATE_FOREGROUND].rchar,
|
||||
task_entry->io[UID_STATE_FOREGROUND].wchar,
|
||||
task_entry->io[UID_STATE_FOREGROUND].read_bytes,
|
||||
task_entry->io[UID_STATE_FOREGROUND].write_bytes,
|
||||
task_entry->io[UID_STATE_BACKGROUND].rchar,
|
||||
task_entry->io[UID_STATE_BACKGROUND].wchar,
|
||||
task_entry->io[UID_STATE_BACKGROUND].read_bytes,
|
||||
task_entry->io[UID_STATE_BACKGROUND].write_bytes,
|
||||
task_entry->io[UID_STATE_FOREGROUND].fsync,
|
||||
task_entry->io[UID_STATE_BACKGROUND].fsync);
|
||||
}
|
||||
}
|
||||
#else
|
||||
static void remove_uid_tasks(struct uid_entry *uid_entry) {};
|
||||
static void set_io_uid_tasks_zero(struct uid_entry *uid_entry) {};
|
||||
static void add_uid_tasks_io_stats(struct uid_entry *uid_entry,
|
||||
struct task_struct *task, int slot) {};
|
||||
static void compute_io_uid_tasks(struct uid_entry *uid_entry) {};
|
||||
static void show_io_uid_tasks(struct seq_file *m,
|
||||
struct uid_entry *uid_entry) {}
|
||||
#endif
|
||||
|
||||
static struct uid_entry *find_uid_entry(uid_t uid)
|
||||
{
|
||||
struct uid_entry *uid_entry;
|
||||
@@ -66,13 +319,15 @@ static struct uid_entry *find_or_register_uid(uid_t uid)
|
||||
return NULL;
|
||||
|
||||
uid_entry->uid = uid;
|
||||
|
||||
#ifdef CONFIG_UID_SYS_STATS_DEBUG
|
||||
hash_init(uid_entry->task_entries);
|
||||
#endif
|
||||
hash_add(hash_table, &uid_entry->hash, uid);
|
||||
|
||||
return uid_entry;
|
||||
}
|
||||
|
||||
static int uid_stat_show(struct seq_file *m, void *v)
|
||||
static int uid_cputime_show(struct seq_file *m, void *v)
|
||||
{
|
||||
struct uid_entry *uid_entry = NULL;
|
||||
struct task_struct *task, *temp;
|
||||
@@ -120,13 +375,13 @@ static int uid_stat_show(struct seq_file *m, void *v)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int uid_stat_open(struct inode *inode, struct file *file)
|
||||
static int uid_cputime_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, uid_stat_show, PDE_DATA(inode));
|
||||
return single_open(file, uid_cputime_show, PDE_DATA(inode));
|
||||
}
|
||||
|
||||
static const struct file_operations uid_stat_fops = {
|
||||
.open = uid_stat_open,
|
||||
static const struct file_operations uid_cputime_fops = {
|
||||
.open = uid_cputime_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
@@ -163,12 +418,14 @@ static ssize_t uid_remove_write(struct file *file,
|
||||
kstrtol(end_uid, 10, &uid_end) != 0) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
rt_mutex_lock(&uid_lock);
|
||||
|
||||
for (; uid_start <= uid_end; uid_start++) {
|
||||
hash_for_each_possible_safe(hash_table, uid_entry, tmp,
|
||||
hash, (uid_t)uid_start) {
|
||||
if (uid_start == uid_entry->uid) {
|
||||
remove_uid_tasks(uid_entry);
|
||||
hash_del(&uid_entry->hash);
|
||||
kfree(uid_entry);
|
||||
}
|
||||
@@ -185,6 +442,177 @@ static const struct file_operations uid_remove_fops = {
|
||||
.write = uid_remove_write,
|
||||
};
|
||||
|
||||
|
||||
static void add_uid_io_stats(struct uid_entry *uid_entry,
|
||||
struct task_struct *task, int slot)
|
||||
{
|
||||
struct io_stats *io_slot = &uid_entry->io[slot];
|
||||
|
||||
io_slot->read_bytes += task->ioac.read_bytes;
|
||||
io_slot->write_bytes += compute_write_bytes(task);
|
||||
io_slot->rchar += task->ioac.rchar;
|
||||
io_slot->wchar += task->ioac.wchar;
|
||||
|
||||
add_uid_tasks_io_stats(uid_entry, task, slot);
|
||||
}
|
||||
|
||||
static void update_io_stats_all_locked(void)
|
||||
{
|
||||
struct uid_entry *uid_entry = NULL;
|
||||
struct task_struct *task, *temp;
|
||||
struct user_namespace *user_ns = current_user_ns();
|
||||
unsigned long bkt;
|
||||
uid_t uid;
|
||||
|
||||
hash_for_each(hash_table, bkt, uid_entry, hash) {
|
||||
memset(&uid_entry->io[UID_STATE_TOTAL_CURR], 0,
|
||||
sizeof(struct io_stats));
|
||||
set_io_uid_tasks_zero(uid_entry);
|
||||
}
|
||||
|
||||
rcu_read_lock();
|
||||
do_each_thread(temp, task) {
|
||||
uid = from_kuid_munged(user_ns, task_uid(task));
|
||||
if (!uid_entry || uid_entry->uid != uid)
|
||||
uid_entry = find_or_register_uid(uid);
|
||||
if (!uid_entry)
|
||||
continue;
|
||||
add_uid_io_stats(uid_entry, task, UID_STATE_TOTAL_CURR);
|
||||
} while_each_thread(temp, task);
|
||||
rcu_read_unlock();
|
||||
|
||||
hash_for_each(hash_table, bkt, uid_entry, 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]);
|
||||
compute_io_uid_tasks(uid_entry);
|
||||
}
|
||||
}
|
||||
|
||||
static void update_io_stats_uid_locked(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));
|
||||
set_io_uid_tasks_zero(uid_entry);
|
||||
|
||||
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_uid_tasks(uid_entry);
|
||||
}
|
||||
|
||||
|
||||
static int uid_io_show(struct seq_file *m, void *v)
|
||||
{
|
||||
struct uid_entry *uid_entry;
|
||||
unsigned long bkt;
|
||||
|
||||
rt_mutex_lock(&uid_lock);
|
||||
|
||||
update_io_stats_all_locked();
|
||||
|
||||
hash_for_each(hash_table, bkt, uid_entry, hash) {
|
||||
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,
|
||||
uid_entry->io[UID_STATE_FOREGROUND].wchar,
|
||||
uid_entry->io[UID_STATE_FOREGROUND].read_bytes,
|
||||
uid_entry->io[UID_STATE_FOREGROUND].write_bytes,
|
||||
uid_entry->io[UID_STATE_BACKGROUND].rchar,
|
||||
uid_entry->io[UID_STATE_BACKGROUND].wchar,
|
||||
uid_entry->io[UID_STATE_BACKGROUND].read_bytes,
|
||||
uid_entry->io[UID_STATE_BACKGROUND].write_bytes,
|
||||
uid_entry->io[UID_STATE_FOREGROUND].fsync,
|
||||
uid_entry->io[UID_STATE_BACKGROUND].fsync);
|
||||
|
||||
show_io_uid_tasks(m, uid_entry);
|
||||
}
|
||||
|
||||
rt_mutex_unlock(&uid_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int uid_io_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, uid_io_show, PDE_DATA(inode));
|
||||
}
|
||||
|
||||
static const struct file_operations uid_io_fops = {
|
||||
.open = uid_io_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
static int uid_procstat_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, NULL, NULL);
|
||||
}
|
||||
|
||||
static ssize_t uid_procstat_write(struct file *file,
|
||||
const char __user *buffer, size_t count, loff_t *ppos)
|
||||
{
|
||||
struct uid_entry *uid_entry;
|
||||
uid_t uid;
|
||||
int argc, state;
|
||||
char input[128];
|
||||
|
||||
if (count >= sizeof(input))
|
||||
return -EINVAL;
|
||||
|
||||
if (copy_from_user(input, buffer, count))
|
||||
return -EFAULT;
|
||||
|
||||
input[count] = '\0';
|
||||
|
||||
argc = sscanf(input, "%u %d", &uid, &state);
|
||||
if (argc != 2)
|
||||
return -EINVAL;
|
||||
|
||||
if (state != UID_STATE_BACKGROUND && state != UID_STATE_FOREGROUND)
|
||||
return -EINVAL;
|
||||
|
||||
rt_mutex_lock(&uid_lock);
|
||||
|
||||
uid_entry = find_or_register_uid(uid);
|
||||
if (!uid_entry) {
|
||||
rt_mutex_unlock(&uid_lock);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (uid_entry->state == state) {
|
||||
rt_mutex_unlock(&uid_lock);
|
||||
return count;
|
||||
}
|
||||
|
||||
update_io_stats_uid_locked(uid_entry);
|
||||
|
||||
uid_entry->state = state;
|
||||
|
||||
rt_mutex_unlock(&uid_lock);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static const struct file_operations uid_procstat_fops = {
|
||||
.open = uid_procstat_open,
|
||||
.release = single_release,
|
||||
.write = uid_procstat_write,
|
||||
};
|
||||
|
||||
static int process_notifier(struct notifier_block *self,
|
||||
unsigned long cmd, void *v)
|
||||
{
|
||||
@@ -208,6 +636,8 @@ static int process_notifier(struct notifier_block *self,
|
||||
uid_entry->utime += utime;
|
||||
uid_entry->stime += stime;
|
||||
|
||||
add_uid_io_stats(uid_entry, task, UID_STATE_DEAD_TASKS);
|
||||
|
||||
exit:
|
||||
rt_mutex_unlock(&uid_lock);
|
||||
return NOTIFY_OK;
|
||||
@@ -217,25 +647,51 @@ static struct notifier_block process_notifier_block = {
|
||||
.notifier_call = process_notifier,
|
||||
};
|
||||
|
||||
static int __init proc_uid_cputime_init(void)
|
||||
static int __init proc_uid_sys_stats_init(void)
|
||||
{
|
||||
hash_init(hash_table);
|
||||
|
||||
parent = proc_mkdir("uid_cputime", NULL);
|
||||
if (!parent) {
|
||||
pr_err("%s: failed to create proc entry\n", __func__);
|
||||
return -ENOMEM;
|
||||
cpu_parent = proc_mkdir("uid_cputime", NULL);
|
||||
if (!cpu_parent) {
|
||||
pr_err("%s: failed to create uid_cputime proc entry\n",
|
||||
__func__);
|
||||
goto err;
|
||||
}
|
||||
|
||||
proc_create_data("remove_uid_range", S_IWUGO, parent, &uid_remove_fops,
|
||||
NULL);
|
||||
proc_create_data("remove_uid_range", 0222, cpu_parent,
|
||||
&uid_remove_fops, NULL);
|
||||
proc_create_data("show_uid_stat", 0444, cpu_parent,
|
||||
&uid_cputime_fops, NULL);
|
||||
|
||||
proc_create_data("show_uid_stat", S_IRUGO, parent, &uid_stat_fops,
|
||||
NULL);
|
||||
io_parent = proc_mkdir("uid_io", NULL);
|
||||
if (!io_parent) {
|
||||
pr_err("%s: failed to create uid_io proc entry\n",
|
||||
__func__);
|
||||
goto err;
|
||||
}
|
||||
|
||||
proc_create_data("stats", 0444, io_parent,
|
||||
&uid_io_fops, NULL);
|
||||
|
||||
proc_parent = proc_mkdir("uid_procstat", NULL);
|
||||
if (!proc_parent) {
|
||||
pr_err("%s: failed to create uid_procstat proc entry\n",
|
||||
__func__);
|
||||
goto err;
|
||||
}
|
||||
|
||||
proc_create_data("set", 0222, proc_parent,
|
||||
&uid_procstat_fops, NULL);
|
||||
|
||||
profile_event_register(PROFILE_TASK_EXIT, &process_notifier_block);
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
remove_proc_subtree("uid_cputime", NULL);
|
||||
remove_proc_subtree("uid_io", NULL);
|
||||
remove_proc_subtree("uid_procstat", NULL);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
early_initcall(proc_uid_cputime_init);
|
||||
early_initcall(proc_uid_sys_stats_init);
|
||||
|
||||
Reference in New Issue
Block a user