| .. | .. |
|---|
| 14 | 14 | */ |
|---|
| 15 | 15 | |
|---|
| 16 | 16 | #include <linux/atomic.h> |
|---|
| 17 | | -#include <linux/cpufreq_times.h> |
|---|
| 18 | 17 | #include <linux/err.h> |
|---|
| 19 | 18 | #include <linux/hashtable.h> |
|---|
| 20 | 19 | #include <linux/init.h> |
|---|
| .. | .. |
|---|
| 78 | 77 | #endif |
|---|
| 79 | 78 | }; |
|---|
| 80 | 79 | |
|---|
| 81 | | -static u64 compute_write_bytes(struct task_struct *task) |
|---|
| 80 | +static u64 compute_write_bytes(struct task_io_accounting *ioac) |
|---|
| 82 | 81 | { |
|---|
| 83 | | - if (task->ioac.write_bytes <= task->ioac.cancelled_write_bytes) |
|---|
| 82 | + if (ioac->write_bytes <= ioac->cancelled_write_bytes) |
|---|
| 84 | 83 | return 0; |
|---|
| 85 | 84 | |
|---|
| 86 | | - return task->ioac.write_bytes - task->ioac.cancelled_write_bytes; |
|---|
| 85 | + return ioac->write_bytes - ioac->cancelled_write_bytes; |
|---|
| 87 | 86 | } |
|---|
| 88 | 87 | |
|---|
| 89 | 88 | static void compute_io_bucket_stats(struct io_stats *io_bucket, |
|---|
| .. | .. |
|---|
| 137 | 136 | |
|---|
| 138 | 137 | /* next the executable file name */ |
|---|
| 139 | 138 | if (mm) { |
|---|
| 140 | | - down_read(&mm->mmap_sem); |
|---|
| 139 | + mmap_write_lock(mm); |
|---|
| 141 | 140 | if (mm->exe_file) { |
|---|
| 142 | 141 | char *pathname = d_path(&mm->exe_file->f_path, buf, |
|---|
| 143 | 142 | unused_len); |
|---|
| .. | .. |
|---|
| 150 | 149 | unused_len--; |
|---|
| 151 | 150 | } |
|---|
| 152 | 151 | } |
|---|
| 153 | | - up_read(&mm->mmap_sem); |
|---|
| 152 | + mmap_write_unlock(mm); |
|---|
| 154 | 153 | } |
|---|
| 155 | 154 | unused_len -= len; |
|---|
| 156 | 155 | |
|---|
| .. | .. |
|---|
| 240 | 239 | } |
|---|
| 241 | 240 | } |
|---|
| 242 | 241 | |
|---|
| 243 | | -static void add_uid_tasks_io_stats(struct uid_entry *uid_entry, |
|---|
| 244 | | - struct task_struct *task, int slot) |
|---|
| 242 | +static void add_uid_tasks_io_stats(struct task_entry *task_entry, |
|---|
| 243 | + struct task_io_accounting *ioac, int slot) |
|---|
| 245 | 244 | { |
|---|
| 246 | | - struct task_entry *task_entry = find_or_register_task(uid_entry, task); |
|---|
| 247 | 245 | struct io_stats *task_io_slot = &task_entry->io[slot]; |
|---|
| 248 | 246 | |
|---|
| 249 | | - task_io_slot->read_bytes += task->ioac.read_bytes; |
|---|
| 250 | | - task_io_slot->write_bytes += compute_write_bytes(task); |
|---|
| 251 | | - task_io_slot->rchar += task->ioac.rchar; |
|---|
| 252 | | - task_io_slot->wchar += task->ioac.wchar; |
|---|
| 253 | | - task_io_slot->fsync += task->ioac.syscfs; |
|---|
| 247 | + task_io_slot->read_bytes += ioac->read_bytes; |
|---|
| 248 | + task_io_slot->write_bytes += compute_write_bytes(ioac); |
|---|
| 249 | + task_io_slot->rchar += ioac->rchar; |
|---|
| 250 | + task_io_slot->wchar += ioac->wchar; |
|---|
| 251 | + task_io_slot->fsync += ioac->syscfs; |
|---|
| 254 | 252 | } |
|---|
| 255 | 253 | |
|---|
| 256 | 254 | static void compute_io_uid_tasks(struct uid_entry *uid_entry) |
|---|
| .. | .. |
|---|
| 291 | 289 | #else |
|---|
| 292 | 290 | static void remove_uid_tasks(struct uid_entry *uid_entry) {}; |
|---|
| 293 | 291 | static void set_io_uid_tasks_zero(struct uid_entry *uid_entry) {}; |
|---|
| 294 | | -static void add_uid_tasks_io_stats(struct uid_entry *uid_entry, |
|---|
| 295 | | - struct task_struct *task, int slot) {}; |
|---|
| 296 | 292 | static void compute_io_uid_tasks(struct uid_entry *uid_entry) {}; |
|---|
| 297 | 293 | static void show_io_uid_tasks(struct seq_file *m, |
|---|
| 298 | 294 | struct uid_entry *uid_entry) {} |
|---|
| .. | .. |
|---|
| 385 | 381 | return single_open(file, uid_cputime_show, PDE_DATA(inode)); |
|---|
| 386 | 382 | } |
|---|
| 387 | 383 | |
|---|
| 388 | | -static const struct file_operations uid_cputime_fops = { |
|---|
| 389 | | - .open = uid_cputime_open, |
|---|
| 390 | | - .read = seq_read, |
|---|
| 391 | | - .llseek = seq_lseek, |
|---|
| 392 | | - .release = single_release, |
|---|
| 384 | +static const struct proc_ops uid_cputime_fops = { |
|---|
| 385 | + .proc_open = uid_cputime_open, |
|---|
| 386 | + .proc_read = seq_read, |
|---|
| 387 | + .proc_lseek = seq_lseek, |
|---|
| 388 | + .proc_release = single_release, |
|---|
| 393 | 389 | }; |
|---|
| 394 | 390 | |
|---|
| 395 | 391 | static int uid_remove_open(struct inode *inode, struct file *file) |
|---|
| .. | .. |
|---|
| 404 | 400 | struct hlist_node *tmp; |
|---|
| 405 | 401 | char uids[128]; |
|---|
| 406 | 402 | char *start_uid, *end_uid = NULL; |
|---|
| 407 | | - uid_t uid_start = 0, uid_end = 0; |
|---|
| 408 | | - u64 uid; |
|---|
| 403 | + long int uid_start = 0, uid_end = 0; |
|---|
| 409 | 404 | |
|---|
| 410 | 405 | if (count >= sizeof(uids)) |
|---|
| 411 | 406 | count = sizeof(uids) - 1; |
|---|
| .. | .. |
|---|
| 420 | 415 | if (!start_uid || !end_uid) |
|---|
| 421 | 416 | return -EINVAL; |
|---|
| 422 | 417 | |
|---|
| 423 | | - if (kstrtouint(start_uid, 10, &uid_start) != 0 || |
|---|
| 424 | | - kstrtouint(end_uid, 10, &uid_end) != 0) { |
|---|
| 418 | + if (kstrtol(start_uid, 10, &uid_start) != 0 || |
|---|
| 419 | + kstrtol(end_uid, 10, &uid_end) != 0) { |
|---|
| 425 | 420 | return -EINVAL; |
|---|
| 426 | 421 | } |
|---|
| 427 | 422 | |
|---|
| 428 | | - /* Also remove uids from /proc/uid_time_in_state */ |
|---|
| 429 | | - cpufreq_task_times_remove_uids(uid_start, uid_end); |
|---|
| 430 | | - |
|---|
| 431 | 423 | rt_mutex_lock(&uid_lock); |
|---|
| 432 | 424 | |
|---|
| 433 | | - for (uid = uid_start; uid <= uid_end; uid++) { |
|---|
| 425 | + for (; uid_start <= uid_end; uid_start++) { |
|---|
| 434 | 426 | hash_for_each_possible_safe(hash_table, uid_entry, tmp, |
|---|
| 435 | | - hash, uid) { |
|---|
| 436 | | - if (uid == uid_entry->uid) { |
|---|
| 427 | + hash, (uid_t)uid_start) { |
|---|
| 428 | + if (uid_start == uid_entry->uid) { |
|---|
| 437 | 429 | remove_uid_tasks(uid_entry); |
|---|
| 438 | 430 | hash_del(&uid_entry->hash); |
|---|
| 439 | 431 | kfree(uid_entry); |
|---|
| .. | .. |
|---|
| 445 | 437 | return count; |
|---|
| 446 | 438 | } |
|---|
| 447 | 439 | |
|---|
| 448 | | -static const struct file_operations uid_remove_fops = { |
|---|
| 449 | | - .open = uid_remove_open, |
|---|
| 450 | | - .release = single_release, |
|---|
| 451 | | - .write = uid_remove_write, |
|---|
| 440 | +static const struct proc_ops uid_remove_fops = { |
|---|
| 441 | + .proc_open = uid_remove_open, |
|---|
| 442 | + .proc_release = single_release, |
|---|
| 443 | + .proc_write = uid_remove_write, |
|---|
| 452 | 444 | }; |
|---|
| 453 | 445 | |
|---|
| 446 | +static void __add_uid_io_stats(struct uid_entry *uid_entry, |
|---|
| 447 | + struct task_io_accounting *ioac, int slot) |
|---|
| 448 | +{ |
|---|
| 449 | + struct io_stats *io_slot = &uid_entry->io[slot]; |
|---|
| 450 | + |
|---|
| 451 | + io_slot->read_bytes += ioac->read_bytes; |
|---|
| 452 | + io_slot->write_bytes += compute_write_bytes(ioac); |
|---|
| 453 | + io_slot->rchar += ioac->rchar; |
|---|
| 454 | + io_slot->wchar += ioac->wchar; |
|---|
| 455 | + io_slot->fsync += ioac->syscfs; |
|---|
| 456 | +} |
|---|
| 454 | 457 | |
|---|
| 455 | 458 | static void add_uid_io_stats(struct uid_entry *uid_entry, |
|---|
| 456 | 459 | struct task_struct *task, int slot) |
|---|
| 457 | 460 | { |
|---|
| 458 | | - struct io_stats *io_slot = &uid_entry->io[slot]; |
|---|
| 461 | + struct task_entry *task_entry __maybe_unused; |
|---|
| 459 | 462 | |
|---|
| 460 | 463 | /* avoid double accounting of dying threads */ |
|---|
| 461 | 464 | if (slot != UID_STATE_DEAD_TASKS && (task->flags & PF_EXITING)) |
|---|
| 462 | 465 | return; |
|---|
| 463 | 466 | |
|---|
| 464 | | - io_slot->read_bytes += task->ioac.read_bytes; |
|---|
| 465 | | - io_slot->write_bytes += compute_write_bytes(task); |
|---|
| 466 | | - io_slot->rchar += task->ioac.rchar; |
|---|
| 467 | | - io_slot->wchar += task->ioac.wchar; |
|---|
| 468 | | - io_slot->fsync += task->ioac.syscfs; |
|---|
| 469 | | - |
|---|
| 470 | | - add_uid_tasks_io_stats(uid_entry, task, slot); |
|---|
| 467 | +#ifdef CONFIG_UID_SYS_STATS_DEBUG |
|---|
| 468 | + task_entry = find_or_register_task(uid_entry, task); |
|---|
| 469 | + add_uid_tasks_io_stats(task_entry, &task->ioac, slot); |
|---|
| 470 | +#endif |
|---|
| 471 | + __add_uid_io_stats(uid_entry, &task->ioac, slot); |
|---|
| 471 | 472 | } |
|---|
| 472 | 473 | |
|---|
| 473 | 474 | static void update_io_stats_all_locked(void) |
|---|
| .. | .. |
|---|
| 564 | 565 | return single_open(file, uid_io_show, PDE_DATA(inode)); |
|---|
| 565 | 566 | } |
|---|
| 566 | 567 | |
|---|
| 567 | | -static const struct file_operations uid_io_fops = { |
|---|
| 568 | | - .open = uid_io_open, |
|---|
| 569 | | - .read = seq_read, |
|---|
| 570 | | - .llseek = seq_lseek, |
|---|
| 571 | | - .release = single_release, |
|---|
| 568 | +static const struct proc_ops uid_io_fops = { |
|---|
| 569 | + .proc_open = uid_io_open, |
|---|
| 570 | + .proc_read = seq_read, |
|---|
| 571 | + .proc_lseek = seq_lseek, |
|---|
| 572 | + .proc_release = single_release, |
|---|
| 572 | 573 | }; |
|---|
| 573 | 574 | |
|---|
| 574 | 575 | static int uid_procstat_open(struct inode *inode, struct file *file) |
|---|
| .. | .. |
|---|
| 621 | 622 | return count; |
|---|
| 622 | 623 | } |
|---|
| 623 | 624 | |
|---|
| 624 | | -static const struct file_operations uid_procstat_fops = { |
|---|
| 625 | | - .open = uid_procstat_open, |
|---|
| 626 | | - .release = single_release, |
|---|
| 627 | | - .write = uid_procstat_write, |
|---|
| 625 | +static const struct proc_ops uid_procstat_fops = { |
|---|
| 626 | + .proc_open = uid_procstat_open, |
|---|
| 627 | + .proc_release = single_release, |
|---|
| 628 | + .proc_write = uid_procstat_write, |
|---|
| 628 | 629 | }; |
|---|
| 630 | + |
|---|
| 631 | +struct update_stats_work { |
|---|
| 632 | + struct work_struct work; |
|---|
| 633 | + uid_t uid; |
|---|
| 634 | +#ifdef CONFIG_UID_SYS_STATS_DEBUG |
|---|
| 635 | + struct task_struct *task; |
|---|
| 636 | +#endif |
|---|
| 637 | + struct task_io_accounting ioac; |
|---|
| 638 | + u64 utime; |
|---|
| 639 | + u64 stime; |
|---|
| 640 | +}; |
|---|
| 641 | + |
|---|
| 642 | +static void update_stats_workfn(struct work_struct *work) |
|---|
| 643 | +{ |
|---|
| 644 | + struct update_stats_work *usw = |
|---|
| 645 | + container_of(work, struct update_stats_work, work); |
|---|
| 646 | + struct uid_entry *uid_entry; |
|---|
| 647 | + struct task_entry *task_entry __maybe_unused; |
|---|
| 648 | + |
|---|
| 649 | + rt_mutex_lock(&uid_lock); |
|---|
| 650 | + uid_entry = find_uid_entry(usw->uid); |
|---|
| 651 | + if (!uid_entry) |
|---|
| 652 | + goto exit; |
|---|
| 653 | + |
|---|
| 654 | + uid_entry->utime += usw->utime; |
|---|
| 655 | + uid_entry->stime += usw->stime; |
|---|
| 656 | + |
|---|
| 657 | +#ifdef CONFIG_UID_SYS_STATS_DEBUG |
|---|
| 658 | + task_entry = find_task_entry(uid_entry, usw->task); |
|---|
| 659 | + if (!task_entry) |
|---|
| 660 | + goto exit; |
|---|
| 661 | + add_uid_tasks_io_stats(task_entry, &usw->ioac, |
|---|
| 662 | + UID_STATE_DEAD_TASKS); |
|---|
| 663 | +#endif |
|---|
| 664 | + __add_uid_io_stats(uid_entry, &usw->ioac, UID_STATE_DEAD_TASKS); |
|---|
| 665 | +exit: |
|---|
| 666 | + rt_mutex_unlock(&uid_lock); |
|---|
| 667 | +#ifdef CONFIG_UID_SYS_STATS_DEBUG |
|---|
| 668 | + put_task_struct(usw->task); |
|---|
| 669 | +#endif |
|---|
| 670 | + kfree(usw); |
|---|
| 671 | +} |
|---|
| 629 | 672 | |
|---|
| 630 | 673 | static int process_notifier(struct notifier_block *self, |
|---|
| 631 | 674 | unsigned long cmd, void *v) |
|---|
| .. | .. |
|---|
| 638 | 681 | if (!task) |
|---|
| 639 | 682 | return NOTIFY_OK; |
|---|
| 640 | 683 | |
|---|
| 641 | | - rt_mutex_lock(&uid_lock); |
|---|
| 642 | 684 | uid = from_kuid_munged(current_user_ns(), task_uid(task)); |
|---|
| 685 | + if (!rt_mutex_trylock(&uid_lock)) { |
|---|
| 686 | + struct update_stats_work *usw; |
|---|
| 687 | + |
|---|
| 688 | + usw = kmalloc(sizeof(struct update_stats_work), GFP_KERNEL); |
|---|
| 689 | + if (usw) { |
|---|
| 690 | + INIT_WORK(&usw->work, update_stats_workfn); |
|---|
| 691 | + usw->uid = uid; |
|---|
| 692 | +#ifdef CONFIG_UID_SYS_STATS_DEBUG |
|---|
| 693 | + usw->task = get_task_struct(task); |
|---|
| 694 | +#endif |
|---|
| 695 | + /* |
|---|
| 696 | + * Copy task->ioac since task might be destroyed before |
|---|
| 697 | + * the work is later performed. |
|---|
| 698 | + */ |
|---|
| 699 | + usw->ioac = task->ioac; |
|---|
| 700 | + task_cputime_adjusted(task, &usw->utime, &usw->stime); |
|---|
| 701 | + schedule_work(&usw->work); |
|---|
| 702 | + } |
|---|
| 703 | + return NOTIFY_OK; |
|---|
| 704 | + } |
|---|
| 705 | + |
|---|
| 643 | 706 | uid_entry = find_or_register_uid(uid); |
|---|
| 644 | 707 | if (!uid_entry) { |
|---|
| 645 | 708 | pr_err("%s: failed to find uid %d\n", __func__, uid); |
|---|