Something went wrong. Try again.
Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628// SPDX-License-Identifier: GPL-2.0/* * linux/kernel/acct.c * * BSD Process Accounting for Linux * * Author: Marco van Wieringen <mvw@planets.elm.net> * * Some code based on ideas and code from: * Thomas K. Dyas <tdyas@eden.rutgers.edu> * * This file implements BSD-style process accounting. Whenever any * process exits, an accounting record of type "struct acct" is * written to the file specified with the acct() system call. It is * up to user-level programs to do useful things with the accounting * log. The kernel just provides the raw accounting information. * * (C) Copyright 1995 - 1997 Marco van Wieringen - ELM Consultancy B.V. * * Plugged two leaks. 1) It didn't return acct_file into the free_filps if * the file happened to be read-only. 2) If the accounting was suspended * due to the lack of space it happily allowed to reopen it and completely * lost the old acct_file. 3/10/98, Al Viro. * * Now we silently close acct_file on attempt to reopen. Cleaned sys_acct(). * XTerms and EMACS are manifestations of pure evil. 21/10/98, AV. * * Fixed a nasty interaction with sys_umount(). If the accounting * was suspeneded we failed to stop it on umount(). Messy. * Another one: remount to readonly didn't stop accounting. * Question: what should we do if we have CAP_SYS_ADMIN but not * CAP_SYS_PACCT? Current code does the following: umount returns -EBUSY * unless we are messing with the root. In that case we are getting a * real mess with do_remount_sb(). 9/11/98, AV. * * Fixed a bunch of races (and pair of leaks). Probably not the best way, * but this one obviously doesn't introduce deadlocks. Later. BTW, found * one race (and leak) in BSD implementation. * OK, that's better. ANOTHER race and leak in BSD variant. There always * is one more bug... 10/11/98, AV. * * Oh, fsck... Oopsable SMP race in do_process_acct() - we must hold * ->mmap_lock to walk the vma list of current->mm. Nasty, since it leaks * a struct file opened for write. Fixed. 2/6/2000, AV. */
#include <linux/slab.h>#include <linux/acct.h>#include <linux/capability.h>#include <linux/tty.h>#include <linux/statfs.h>#include <linux/jiffies.h>#include <linux/syscalls.h>#include <linux/namei.h>#include <linux/sched/cputime.h>
#include <asm/div64.h>#include <linux/pid_namespace.h>#include <linux/fs_pin.h>
/* * These constants control the amount of freespace that suspend and * resume the process accounting system, and the time delay between * each check. * Turned into sysctl-controllable parameters. AV, 12/11/98 */
static int acct_parm[3] = {4, 2, 30};#define RESUME (acct_parm[0]) /* >foo% free space - resume */#define SUSPEND (acct_parm[1]) /* <foo% free space - suspend */#define ACCT_TIMEOUT (acct_parm[2]) /* foo second timeout between checks */
#ifdef CONFIG_SYSCTLstatic const struct ctl_table kern_acct_table[] = { { .procname = "acct", .data = &acct_parm, .maxlen = 3*sizeof(int), .mode = 0644, .proc_handler = proc_dointvec, },};
static __init int kernel_acct_sysctls_init(void){ register_sysctl_init("kernel", kern_acct_table); return 0;}late_initcall(kernel_acct_sysctls_init);#endif /* CONFIG_SYSCTL */
/* * External references and all of the globals. */
struct bsd_acct_struct { struct fs_pin pin; atomic_long_t count; struct rcu_head rcu; struct mutex lock; bool active; bool check_space; unsigned long needcheck; struct file *file; struct pid_namespace *ns; struct work_struct work; struct completion done; acct_t ac;};
static void fill_ac(struct bsd_acct_struct *acct);static void acct_write_process(struct bsd_acct_struct *acct);
/* * Check the amount of free space and suspend/resume accordingly. */static bool check_free_space(struct bsd_acct_struct *acct){ struct kstatfs sbuf;
if (!acct->check_space) return acct->active;
/* May block */ if (vfs_statfs(&acct->file->f_path, &sbuf)) return acct->active;
if (acct->active) { u64 suspend = sbuf.f_blocks * SUSPEND; do_div(suspend, 100); if (sbuf.f_bavail <= suspend) { acct->active = false; pr_info("Process accounting paused\n"); } } else { u64 resume = sbuf.f_blocks * RESUME; do_div(resume, 100); if (sbuf.f_bavail >= resume) { acct->active = true; pr_info("Process accounting resumed\n"); } }
acct->needcheck = jiffies + ACCT_TIMEOUT*HZ; return acct->active;}
static void acct_put(struct bsd_acct_struct *p){ if (atomic_long_dec_and_test(&p->count)) kfree_rcu(p, rcu);}
static inline struct bsd_acct_struct *to_acct(struct fs_pin *p){ return p ? container_of(p, struct bsd_acct_struct, pin) : NULL;}
static struct bsd_acct_struct *acct_get(struct pid_namespace *ns){ struct bsd_acct_struct *res;again: smp_rmb(); rcu_read_lock(); res = to_acct(READ_ONCE(ns->bacct)); if (!res) { rcu_read_unlock(); return NULL; } if (!atomic_long_inc_not_zero(&res->count)) { rcu_read_unlock(); cpu_relax(); goto again; } rcu_read_unlock(); mutex_lock(&res->lock); if (res != to_acct(READ_ONCE(ns->bacct))) { mutex_unlock(&res->lock); acct_put(res); goto again; } return res;}
static void acct_pin_kill(struct fs_pin *pin){ struct bsd_acct_struct *acct = to_acct(pin); mutex_lock(&acct->lock); /* * Fill the accounting struct with the exiting task's info * before punting to the workqueue. */ fill_ac(acct); schedule_work(&acct->work); wait_for_completion(&acct->done); cmpxchg(&acct->ns->bacct, pin, NULL); mutex_unlock(&acct->lock); pin_remove(pin); acct_put(acct);}
static void close_work(struct work_struct *work){ struct bsd_acct_struct *acct = container_of(work, struct bsd_acct_struct, work); struct file *file = acct->file;
/* We were fired by acct_pin_kill() which holds acct->lock. */ acct_write_process(acct); if (file->f_op->flush) file->f_op->flush(file, NULL); __fput_sync(file); complete(&acct->done);}
DEFINE_FREE(fput_sync, struct file *, if (!IS_ERR_OR_NULL(_T)) __fput_sync(_T))static int acct_on(const char __user *name){ /* Difference from BSD - they don't do O_APPEND */ const int open_flags = O_WRONLY|O_APPEND|O_LARGEFILE; struct pid_namespace *ns = task_active_pid_ns(current); struct file *original_file __free(fput) = NULL; // in that order struct path internal __free(path_put) = {}; // in that order struct file *file __free(fput_sync) = NULL; // in that order struct bsd_acct_struct *acct; struct vfsmount *mnt; struct fs_pin *old;
CLASS(filename, pathname)(name); original_file = file_open_name(pathname, open_flags, 0); if (IS_ERR(original_file)) return PTR_ERR(original_file);
mnt = mnt_clone_internal(&original_file->f_path); if (IS_ERR(mnt)) return PTR_ERR(mnt);
internal.mnt = mnt; internal.dentry = dget(mnt->mnt_root);
file = dentry_open(&internal, open_flags, current_cred()); if (IS_ERR(file)) return PTR_ERR(file);
if (!S_ISREG(file_inode(file)->i_mode)) return -EACCES;
/* Exclude kernel internal filesystems. */ if (file_inode(file)->i_sb->s_flags & (SB_NOUSER | SB_KERNMOUNT)) return -EINVAL;
/* Exclude procfs and sysfs. */ if (file_inode(file)->i_sb->s_type->fs_flags & FS_USERNS_MOUNT_RESTRICTED) return -EINVAL;
if (!(file->f_mode & FMODE_CAN_WRITE)) return -EIO;
acct = kzalloc_obj(struct bsd_acct_struct); if (!acct) return -ENOMEM;
atomic_long_set(&acct->count, 1); init_fs_pin(&acct->pin, acct_pin_kill); acct->file = no_free_ptr(file); acct->needcheck = jiffies; acct->ns = ns; mutex_init(&acct->lock); INIT_WORK(&acct->work, close_work); init_completion(&acct->done); mutex_lock_nested(&acct->lock, 1); /* nobody has seen it yet */ pin_insert(&acct->pin, original_file->f_path.mnt);
rcu_read_lock(); old = xchg(&ns->bacct, &acct->pin); mutex_unlock(&acct->lock); pin_kill(old); return 0;}
static DEFINE_MUTEX(acct_on_mutex);
/** * sys_acct - enable/disable process accounting * @name: file name for accounting records or NULL to shutdown accounting * * sys_acct() is the only system call needed to implement process * accounting. It takes the name of the file where accounting records * should be written. If the filename is NULL, accounting will be * shutdown. * * Returns: 0 for success or negative errno values for failure. */SYSCALL_DEFINE1(acct, const char __user *, name){ int error = 0;
if (!capable(CAP_SYS_PACCT)) return -EPERM;
if (name) { mutex_lock(&acct_on_mutex); error = acct_on(name); mutex_unlock(&acct_on_mutex); } else { rcu_read_lock(); pin_kill(task_active_pid_ns(current)->bacct); }
return error;}
void acct_exit_ns(struct pid_namespace *ns){ rcu_read_lock(); pin_kill(ns->bacct);}
/* * encode an u64 into a comp_t * * This routine has been adopted from the encode_comp_t() function in * the kern_acct.c file of the FreeBSD operating system. The encoding * is a 13-bit fraction with a 3-bit (base 8) exponent. */
#define MANTSIZE 13 /* 13 bit mantissa. */#define EXPSIZE 3 /* Base 8 (3 bit) exponent. */#define MAXFRACT ((1 << MANTSIZE) - 1) /* Maximum fractional value. */
static comp_t encode_comp_t(u64 value){ int exp, rnd;
exp = rnd = 0; while (value > MAXFRACT) { rnd = value & (1 << (EXPSIZE - 1)); /* Round up? */ value >>= EXPSIZE; /* Base 8 exponent == 3 bit shift. */ exp++; }
/* * If we need to round up, do it (and handle overflow correctly). */ if (rnd && (++value > MAXFRACT)) { value >>= EXPSIZE; exp++; }
if (exp > (((comp_t) ~0U) >> MANTSIZE)) return (comp_t) ~0U; /* * Clean it up and polish it off. */ exp <<= MANTSIZE; /* Shift the exponent into place */ exp += value; /* and add on the mantissa. */ return exp;}
#if ACCT_VERSION == 1 || ACCT_VERSION == 2/* * encode an u64 into a comp2_t (24 bits) * * Format: 5 bit base 2 exponent, 20 bits mantissa. * The leading bit of the mantissa is not stored, but implied for * non-zero exponents. * Largest encodable value is 50 bits. */
#define MANTSIZE2 20 /* 20 bit mantissa. */#define EXPSIZE2 5 /* 5 bit base 2 exponent. */#define MAXFRACT2 ((1ul << MANTSIZE2) - 1) /* Maximum fractional value. */#define MAXEXP2 ((1 << EXPSIZE2) - 1) /* Maximum exponent. */
static comp2_t encode_comp2_t(u64 value){ int exp, rnd;
exp = (value > (MAXFRACT2>>1)); rnd = 0; while (value > MAXFRACT2) { rnd = value & 1; value >>= 1; exp++; }
/* * If we need to round up, do it (and handle overflow correctly). */ if (rnd && (++value > MAXFRACT2)) { value >>= 1; exp++; }
if (exp > MAXEXP2) { /* Overflow. Return largest representable number instead. */ return (1ul << (MANTSIZE2+EXPSIZE2-1)) - 1; } else { return (value & (MAXFRACT2>>1)) | (exp << (MANTSIZE2-1)); }}#elif ACCT_VERSION == 3/* * encode an u64 into a 32 bit IEEE float */static u32 encode_float(u64 value){ unsigned exp = 190; unsigned u;
if (value == 0) return 0; while ((s64)value > 0) { value <<= 1; exp--; } u = (u32)(value >> 40) & 0x7fffffu; return u | (exp << 23);}#endif
/* * Write an accounting entry for an exiting process * * The acct_process() call is the workhorse of the process * accounting system. The struct acct is built here and then written * into the accounting file. This function should only be called from * do_exit() or when switching to a different output file. */
static void fill_ac(struct bsd_acct_struct *acct){ struct pacct_struct *pacct = ¤t->signal->pacct; struct file *file = acct->file; acct_t *ac = &acct->ac; u64 elapsed, run_time; time64_t btime; struct tty_struct *tty;
lockdep_assert_held(&acct->lock);
if (time_is_after_jiffies(acct->needcheck)) { acct->check_space = false;
/* Don't fill in @ac if nothing will be written. */ if (!acct->active) return; } else { acct->check_space = true; }
/* * Fill the accounting struct with the needed info as recorded * by the different kernel functions. */ memset(ac, 0, sizeof(acct_t));
ac->ac_version = ACCT_VERSION | ACCT_BYTEORDER; strscpy(ac->ac_comm, current->comm, sizeof(ac->ac_comm));
/* calculate run_time in nsec*/ run_time = ktime_get_ns(); run_time -= current->group_leader->start_time; /* convert nsec -> AHZ */ elapsed = nsec_to_AHZ(run_time);#if ACCT_VERSION == 3 ac->ac_etime = encode_float(elapsed);#else ac->ac_etime = encode_comp_t(elapsed < (unsigned long) -1l ? (unsigned long) elapsed : (unsigned long) -1l);#endif#if ACCT_VERSION == 1 || ACCT_VERSION == 2 { /* new enlarged etime field */ comp2_t etime = encode_comp2_t(elapsed);
ac->ac_etime_hi = etime >> 16; ac->ac_etime_lo = (u16) etime; }#endif do_div(elapsed, AHZ); btime = ktime_get_real_seconds() - elapsed; ac->ac_btime = clamp_t(time64_t, btime, 0, U32_MAX);#if ACCT_VERSION == 2 ac->ac_ahz = AHZ;#endif
spin_lock_irq(¤t->sighand->siglock); tty = current->signal->tty; /* Safe as we hold the siglock */ ac->ac_tty = tty ? old_encode_dev(tty_devnum(tty)) : 0; ac->ac_utime = encode_comp_t(nsec_to_AHZ(pacct->ac_utime)); ac->ac_stime = encode_comp_t(nsec_to_AHZ(pacct->ac_stime)); ac->ac_flag = pacct->ac_flag; ac->ac_mem = encode_comp_t(pacct->ac_mem); ac->ac_minflt = encode_comp_t(pacct->ac_minflt); ac->ac_majflt = encode_comp_t(pacct->ac_majflt); ac->ac_exitcode = pacct->ac_exitcode; spin_unlock_irq(¤t->sighand->siglock);
/* we really need to bite the bullet and change layout */ ac->ac_uid = from_kuid_munged(file->f_cred->user_ns, current_uid()); ac->ac_gid = from_kgid_munged(file->f_cred->user_ns, current_gid());#if ACCT_VERSION == 1 || ACCT_VERSION == 2 /* backward-compatible 16 bit fields */ ac->ac_uid16 = ac->ac_uid; ac->ac_gid16 = ac->ac_gid;#elif ACCT_VERSION == 3 { struct pid_namespace *ns = acct->ns;
ac->ac_pid = task_tgid_nr_ns(current, ns); rcu_read_lock(); ac->ac_ppid = task_tgid_nr_ns(rcu_dereference(current->real_parent), ns); rcu_read_unlock(); }#endif}
static void acct_write_process(struct bsd_acct_struct *acct){ struct file *file = acct->file; acct_t *ac = &acct->ac;
/* Perform file operations on behalf of whoever enabled accounting */ scoped_with_creds(file->f_cred) { /* * First check to see if there is enough free_space to continue * the process accounting system. Then get freeze protection. If * the fs is frozen, just skip the write as we could deadlock * the system otherwise. */ if (check_free_space(acct) && file_start_write_trylock(file)) { /* it's been opened O_APPEND, so position is irrelevant */ loff_t pos = 0; __kernel_write(file, ac, sizeof(acct_t), &pos); file_end_write(file); } }}
static void do_acct_process(struct bsd_acct_struct *acct){ unsigned long flim;
/* Accounting records are not subject to resource limits. */ flim = rlimit(RLIMIT_FSIZE); current->signal->rlim[RLIMIT_FSIZE].rlim_cur = RLIM_INFINITY; fill_ac(acct); acct_write_process(acct); current->signal->rlim[RLIMIT_FSIZE].rlim_cur = flim;}
/** * acct_collect - collect accounting information into pacct_struct * @exitcode: task exit code * @group_dead: not 0, if this thread is the last one in the process. */void acct_collect(long exitcode, int group_dead){ struct pacct_struct *pacct = ¤t->signal->pacct; u64 utime, stime; unsigned long vsize = 0;
if (group_dead && current->mm) { struct mm_struct *mm = current->mm; VMA_ITERATOR(vmi, mm, 0); struct vm_area_struct *vma;
mmap_read_lock(mm); for_each_vma(vmi, vma) vsize += vma->vm_end - vma->vm_start; mmap_read_unlock(mm); }
spin_lock_irq(¤t->sighand->siglock); if (group_dead) pacct->ac_mem = vsize / 1024; if (thread_group_leader(current)) { pacct->ac_exitcode = exitcode; if (current->flags & PF_FORKNOEXEC) pacct->ac_flag |= AFORK; } if (current->flags & PF_SUPERPRIV) pacct->ac_flag |= ASU; if (current->flags & PF_DUMPCORE) pacct->ac_flag |= ACORE; if (current->flags & PF_SIGNALED) pacct->ac_flag |= AXSIG;
task_cputime(current, &utime, &stime); pacct->ac_utime += utime; pacct->ac_stime += stime; pacct->ac_minflt += current->min_flt; pacct->ac_majflt += current->maj_flt; spin_unlock_irq(¤t->sighand->siglock);}
static void slow_acct_process(struct pid_namespace *ns){ for ( ; ns; ns = ns->parent) { struct bsd_acct_struct *acct = acct_get(ns); if (acct) { do_acct_process(acct); mutex_unlock(&acct->lock); acct_put(acct); } }}
/** * acct_process - handles process accounting for an exiting task */void acct_process(void){ struct pid_namespace *ns;
/* * This loop is safe lockless, since current is still * alive and holds its namespace, which in turn holds * its parent. */ for (ns = task_active_pid_ns(current); ns != NULL; ns = ns->parent) { if (ns->bacct) break; } if (unlikely(ns)) slow_acct_process(ns);}