acct.c 15.4 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
/*
 *  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 with sys_umount(). If the accointing
 *  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_sem 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/mm.h>
#include <linux/slab.h>
#include <linux/acct.h>
49
#include <linux/capability.h>
Linus Torvalds's avatar
Linus Torvalds committed
50 51 52 53 54 55 56
#include <linux/file.h>
#include <linux/tty.h>
#include <linux/security.h>
#include <linux/vfs.h>
#include <linux/jiffies.h>
#include <linux/times.h>
#include <linux/syscalls.h>
57
#include <linux/mount.h>
58
#include <linux/uaccess.h>
59 60
#include <linux/sched/cputime.h>

Linus Torvalds's avatar
Linus Torvalds committed
61 62
#include <asm/div64.h>
#include <linux/blkdev.h> /* sector_div */
63
#include <linux/pid_namespace.h>
Al Viro's avatar
Al Viro committed
64
#include <linux/fs_pin.h>
Linus Torvalds's avatar
Linus Torvalds committed
65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81

/*
 * 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
 */

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 */

/*
 * External references and all of the globals.
 */

Al Viro's avatar
Al Viro committed
82 83
struct bsd_acct_struct {
	struct fs_pin		pin;
84 85
	atomic_long_t		count;
	struct rcu_head		rcu;
Al Viro's avatar
Al Viro committed
86
	struct mutex		lock;
Al Viro's avatar
Al Viro committed
87 88
	int			active;
	unsigned long		needcheck;
Linus Torvalds's avatar
Linus Torvalds committed
89
	struct file		*file;
90
	struct pid_namespace	*ns;
91 92
	struct work_struct	work;
	struct completion	done;
Linus Torvalds's avatar
Linus Torvalds committed
93 94
};

Al Viro's avatar
Al Viro committed
95 96
static void do_acct_process(struct bsd_acct_struct *acct);

Linus Torvalds's avatar
Linus Torvalds committed
97 98 99
/*
 * Check the amount of free space and suspend/resume accordingly.
 */
Al Viro's avatar
Al Viro committed
100
static int check_free_space(struct bsd_acct_struct *acct)
Linus Torvalds's avatar
Linus Torvalds committed
101 102 103
{
	struct kstatfs sbuf;

Al Viro's avatar
Al Viro committed
104
	if (time_is_before_jiffies(acct->needcheck))
Linus Torvalds's avatar
Linus Torvalds committed
105 106 107
		goto out;

	/* May block */
Al Viro's avatar
Al Viro committed
108
	if (vfs_statfs(&acct->file->f_path, &sbuf))
Linus Torvalds's avatar
Linus Torvalds committed
109 110
		goto out;

111
	if (acct->active) {
Al Viro's avatar
Al Viro committed
112 113 114
		u64 suspend = sbuf.f_blocks * SUSPEND;
		do_div(suspend, 100);
		if (sbuf.f_bavail <= suspend) {
115
			acct->active = 0;
116
			pr_info("Process accounting paused\n");
Linus Torvalds's avatar
Linus Torvalds committed
117 118
		}
	} else {
Al Viro's avatar
Al Viro committed
119 120 121
		u64 resume = sbuf.f_blocks * RESUME;
		do_div(resume, 100);
		if (sbuf.f_bavail >= resume) {
122
			acct->active = 1;
123
			pr_info("Process accounting resumed\n");
Linus Torvalds's avatar
Linus Torvalds committed
124 125 126
		}
	}

Al Viro's avatar
Al Viro committed
127
	acct->needcheck = jiffies + ACCT_TIMEOUT*HZ;
Linus Torvalds's avatar
Linus Torvalds committed
128
out:
Al Viro's avatar
Al Viro committed
129
	return acct->active;
Linus Torvalds's avatar
Linus Torvalds committed
130 131
}

Al Viro's avatar
Al Viro committed
132 133
static void acct_put(struct bsd_acct_struct *p)
{
134 135
	if (atomic_long_dec_and_test(&p->count))
		kfree_rcu(p, rcu);
Al Viro's avatar
Al Viro committed
136 137
}

Al Viro's avatar
Al Viro committed
138 139 140 141 142
static inline struct bsd_acct_struct *to_acct(struct fs_pin *p)
{
	return p ? container_of(p, struct bsd_acct_struct, pin) : NULL;
}

Al Viro's avatar
Al Viro committed
143
static struct bsd_acct_struct *acct_get(struct pid_namespace *ns)
Al Viro's avatar
Al Viro committed
144 145 146
{
	struct bsd_acct_struct *res;
again:
147 148
	smp_rmb();
	rcu_read_lock();
Al Viro's avatar
Al Viro committed
149
	res = to_acct(ACCESS_ONCE(ns->bacct));
150 151
	if (!res) {
		rcu_read_unlock();
Al Viro's avatar
Al Viro committed
152
		return NULL;
Linus Torvalds's avatar
Linus Torvalds committed
153
	}
154
	if (!atomic_long_inc_not_zero(&res->count)) {
Al Viro's avatar
Al Viro committed
155 156
		rcu_read_unlock();
		cpu_relax();
Al Viro's avatar
Al Viro committed
157
		goto again;
Al Viro's avatar
Al Viro committed
158 159 160
	}
	rcu_read_unlock();
	mutex_lock(&res->lock);
Al Viro's avatar
Al Viro committed
161
	if (res != to_acct(ACCESS_ONCE(ns->bacct))) {
Al Viro's avatar
Al Viro committed
162
		mutex_unlock(&res->lock);
Al Viro's avatar
Al Viro committed
163
		acct_put(res);
Al Viro's avatar
Al Viro committed
164 165
		goto again;
	}
Al Viro's avatar
Al Viro committed
166 167 168
	return res;
}

Al Viro's avatar
Al Viro committed
169 170 171 172 173 174 175 176 177 178 179 180 181
static void acct_pin_kill(struct fs_pin *pin)
{
	struct bsd_acct_struct *acct = to_acct(pin);
	mutex_lock(&acct->lock);
	do_acct_process(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);
}

182 183 184 185 186 187 188 189 190 191
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;
	if (file->f_op->flush)
		file->f_op->flush(file, NULL);
	__fput_sync(file);
	complete(&acct->done);
}

192
static int acct_on(struct filename *pathname)
193 194
{
	struct file *file;
Al Viro's avatar
Al Viro committed
195
	struct vfsmount *mnt, *internal;
Al Viro's avatar
Al Viro committed
196
	struct pid_namespace *ns = task_active_pid_ns(current);
Al Viro's avatar
Al Viro committed
197 198
	struct bsd_acct_struct *acct;
	struct fs_pin *old;
Al Viro's avatar
Al Viro committed
199
	int err;
Al Viro's avatar
Al Viro committed
200 201 202 203

	acct = kzalloc(sizeof(struct bsd_acct_struct), GFP_KERNEL);
	if (!acct)
		return -ENOMEM;
204 205

	/* Difference from BSD - they don't do O_APPEND */
206
	file = file_open_name(pathname, O_WRONLY|O_APPEND|O_LARGEFILE, 0);
Al Viro's avatar
Al Viro committed
207 208
	if (IS_ERR(file)) {
		kfree(acct);
209
		return PTR_ERR(file);
Al Viro's avatar
Al Viro committed
210
	}
211

Al Viro's avatar
Al Viro committed
212
	if (!S_ISREG(file_inode(file)->i_mode)) {
Al Viro's avatar
Al Viro committed
213
		kfree(acct);
214 215 216 217
		filp_close(file, NULL);
		return -EACCES;
	}

Al Viro's avatar
Al Viro committed
218
	if (!(file->f_mode & FMODE_CAN_WRITE)) {
Al Viro's avatar
Al Viro committed
219
		kfree(acct);
220 221 222
		filp_close(file, NULL);
		return -EIO;
	}
Al Viro's avatar
Al Viro committed
223 224 225 226 227 228 229 230 231 232 233 234 235 236 237
	internal = mnt_clone_internal(&file->f_path);
	if (IS_ERR(internal)) {
		kfree(acct);
		filp_close(file, NULL);
		return PTR_ERR(internal);
	}
	err = mnt_want_write(internal);
	if (err) {
		mntput(internal);
		kfree(acct);
		filp_close(file, NULL);
		return err;
	}
	mnt = file->f_path.mnt;
	file->f_path.mnt = internal;
238

239
	atomic_long_set(&acct->count, 1);
Al Viro's avatar
Al Viro committed
240
	init_fs_pin(&acct->pin, acct_pin_kill);
Al Viro's avatar
Al Viro committed
241 242 243 244
	acct->file = file;
	acct->needcheck = jiffies;
	acct->ns = ns;
	mutex_init(&acct->lock);
Al Viro's avatar
Al Viro committed
245 246
	INIT_WORK(&acct->work, close_work);
	init_completion(&acct->done);
Al Viro's avatar
Al Viro committed
247 248
	mutex_lock_nested(&acct->lock, 1);	/* nobody has seen it yet */
	pin_insert(&acct->pin, mnt);
249

Al Viro's avatar
Al Viro committed
250 251
	rcu_read_lock();
	old = xchg(&ns->bacct, &acct->pin);
Al Viro's avatar
Al Viro committed
252
	mutex_unlock(&acct->lock);
Al Viro's avatar
Al Viro committed
253
	pin_kill(old);
Al Viro's avatar
Al Viro committed
254 255
	mnt_drop_write(mnt);
	mntput(mnt);
256 257 258
	return 0;
}

Al Viro's avatar
Al Viro committed
259 260
static DEFINE_MUTEX(acct_on_mutex);

261 262 263 264 265 266 267 268 269 270
/**
 * sys_acct - enable/disable process accounting
 * @name: file name for accounting records or NULL to shutdown accounting
 *
 * Returns 0 for success or negative errno values for failure.
 *
 * 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.
Linus Torvalds's avatar
Linus Torvalds committed
271
 */
272
SYSCALL_DEFINE1(acct, const char __user *, name)
Linus Torvalds's avatar
Linus Torvalds committed
273
{
Eric Paris's avatar
Eric Paris committed
274
	int error = 0;
Linus Torvalds's avatar
Linus Torvalds committed
275 276 277 278 279

	if (!capable(CAP_SYS_PACCT))
		return -EPERM;

	if (name) {
280
		struct filename *tmp = getname(name);
281

282
		if (IS_ERR(tmp))
Paul McQuade's avatar
Paul McQuade committed
283
			return PTR_ERR(tmp);
Al Viro's avatar
Al Viro committed
284
		mutex_lock(&acct_on_mutex);
285
		error = acct_on(tmp);
Al Viro's avatar
Al Viro committed
286
		mutex_unlock(&acct_on_mutex);
Linus Torvalds's avatar
Linus Torvalds committed
287
		putname(tmp);
288
	} else {
Al Viro's avatar
Al Viro committed
289 290
		rcu_read_lock();
		pin_kill(task_active_pid_ns(current)->bacct);
Linus Torvalds's avatar
Linus Torvalds committed
291
	}
Eric Paris's avatar
Eric Paris committed
292

293 294
	return error;
}
Linus Torvalds's avatar
Linus Torvalds committed
295

296 297
void acct_exit_ns(struct pid_namespace *ns)
{
Al Viro's avatar
Al Viro committed
298 299
	rcu_read_lock();
	pin_kill(ns->bacct);
Linus Torvalds's avatar
Linus Torvalds committed
300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325
}

/*
 *  encode an unsigned long 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(unsigned long 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++;
	}

	/*
326 327
	 * If we need to round up, do it (and handle overflow correctly).
	 */
Linus Torvalds's avatar
Linus Torvalds committed
328 329 330 331 332 333
	if (rnd && (++value > MAXFRACT)) {
		value >>= EXPSIZE;
		exp++;
	}

	/*
334 335
	 * Clean it up and polish it off.
	 */
Linus Torvalds's avatar
Linus Torvalds committed
336 337 338 339 340
	exp <<= MANTSIZE;		/* Shift the exponent into place */
	exp += value;			/* and add on the mantissa. */
	return exp;
}

341
#if ACCT_VERSION == 1 || ACCT_VERSION == 2
Linus Torvalds's avatar
Linus Torvalds committed
342 343 344 345 346 347 348 349 350 351 352 353
/*
 * 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. */
354
#define MAXEXP2         ((1 << EXPSIZE2) - 1)    /* Maximum exponent. */
Linus Torvalds's avatar
Linus Torvalds committed
355 356 357

static comp2_t encode_comp2_t(u64 value)
{
358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381
	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));
	}
Linus Torvalds's avatar
Linus Torvalds committed
382 383 384
}
#endif

385
#if ACCT_VERSION == 3
Linus Torvalds's avatar
Linus Torvalds committed
386 387 388 389 390 391 392 393
/*
 * encode an u64 into a 32 bit IEEE float
 */
static u32 encode_float(u64 value)
{
	unsigned exp = 190;
	unsigned u;

394 395 396
	if (value == 0)
		return 0;
	while ((s64)value > 0) {
Linus Torvalds's avatar
Linus Torvalds committed
397 398 399 400 401 402 403 404 405 406 407 408 409 410
		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
411
 *  do_exit() or when switching to a different output file.
Linus Torvalds's avatar
Linus Torvalds committed
412 413
 */

414
static void fill_ac(acct_t *ac)
Linus Torvalds's avatar
Linus Torvalds committed
415
{
416
	struct pacct_struct *pacct = &current->signal->pacct;
417
	u64 elapsed, run_time;
418
	struct tty_struct *tty;
Linus Torvalds's avatar
Linus Torvalds committed
419 420 421 422 423

	/*
	 * Fill the accounting struct with the needed info as recorded
	 * by the different kernel functions.
	 */
424
	memset(ac, 0, sizeof(acct_t));
Linus Torvalds's avatar
Linus Torvalds committed
425

426 427
	ac->ac_version = ACCT_VERSION | ACCT_BYTEORDER;
	strlcpy(ac->ac_comm, current->comm, sizeof(ac->ac_comm));
Linus Torvalds's avatar
Linus Torvalds committed
428 429

	/* calculate run_time in nsec*/
430 431
	run_time = ktime_get_ns();
	run_time -= current->group_leader->start_time;
Linus Torvalds's avatar
Linus Torvalds committed
432 433
	/* convert nsec -> AHZ */
	elapsed = nsec_to_AHZ(run_time);
434
#if ACCT_VERSION == 3
435
	ac->ac_etime = encode_float(elapsed);
Linus Torvalds's avatar
Linus Torvalds committed
436
#else
437
	ac->ac_etime = encode_comp_t(elapsed < (unsigned long) -1l ?
438
				(unsigned long) elapsed : (unsigned long) -1l);
Linus Torvalds's avatar
Linus Torvalds committed
439
#endif
440
#if ACCT_VERSION == 1 || ACCT_VERSION == 2
Linus Torvalds's avatar
Linus Torvalds committed
441 442 443
	{
		/* new enlarged etime field */
		comp2_t etime = encode_comp2_t(elapsed);
444

445 446
		ac->ac_etime_hi = etime >> 16;
		ac->ac_etime_lo = (u16) etime;
Linus Torvalds's avatar
Linus Torvalds committed
447 448 449
	}
#endif
	do_div(elapsed, AHZ);
450 451 452 453 454 455 456 457
	ac->ac_btime = get_seconds() - elapsed;
#if ACCT_VERSION==2
	ac->ac_ahz = AHZ;
#endif

	spin_lock_irq(&current->sighand->siglock);
	tty = current->signal->tty;	/* Safe as we hold the siglock */
	ac->ac_tty = tty ? old_encode_dev(tty_devnum(tty)) : 0;
458 459
	ac->ac_utime = encode_comp_t(nsec_to_AHZ(pacct->ac_utime));
	ac->ac_stime = encode_comp_t(nsec_to_AHZ(pacct->ac_stime));
460 461 462 463 464 465 466 467 468 469
	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(&current->sighand->siglock);
}
/*
 *  do_acct_process does all actual work. Caller holds the reference to file.
 */
Al Viro's avatar
Al Viro committed
470
static void do_acct_process(struct bsd_acct_struct *acct)
471 472 473 474
{
	acct_t ac;
	unsigned long flim;
	const struct cred *orig_cred;
Al Viro's avatar
Al Viro committed
475
	struct file *file = acct->file;
476 477 478 479 480 481 482 483 484 485 486 487 488

	/*
	 * Accounting records are not subject to resource limits.
	 */
	flim = current->signal->rlim[RLIMIT_FSIZE].rlim_cur;
	current->signal->rlim[RLIMIT_FSIZE].rlim_cur = RLIM_INFINITY;
	/* Perform file operations on behalf of whoever enabled accounting */
	orig_cred = override_creds(file->f_cred);

	/*
	 * First check to see if there is enough free_space to continue
	 * the process accounting system.
	 */
Al Viro's avatar
Al Viro committed
489
	if (!check_free_space(acct))
490 491 492
		goto out;

	fill_ac(&ac);
Linus Torvalds's avatar
Linus Torvalds committed
493
	/* we really need to bite the bullet and change layout */
494 495
	ac.ac_uid = from_kuid_munged(file->f_cred->user_ns, orig_cred->uid);
	ac.ac_gid = from_kgid_munged(file->f_cred->user_ns, orig_cred->gid);
496
#if ACCT_VERSION == 1 || ACCT_VERSION == 2
Linus Torvalds's avatar
Linus Torvalds committed
497
	/* backward-compatible 16 bit fields */
498 499
	ac.ac_uid16 = ac.ac_uid;
	ac.ac_gid16 = ac.ac_gid;
Linus Torvalds's avatar
Linus Torvalds committed
500
#endif
501
#if ACCT_VERSION == 3
Ying Xue's avatar
Ying Xue committed
502 503 504 505 506 507 508 509 510
	{
		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();
	}
Linus Torvalds's avatar
Linus Torvalds committed
511
#endif
512 513 514 515
	/*
	 * Get freeze protection. If the fs is frozen, just skip the write
	 * as we could deadlock the system otherwise.
	 */
Al Viro's avatar
Al Viro committed
516 517 518 519 520 521
	if (file_start_write_trylock(file)) {
		/* it's been opened O_APPEND, so position is irrelevant */
		loff_t pos = 0;
		__kernel_write(file, (char *)&ac, sizeof(acct_t), &pos);
		file_end_write(file);
	}
522
out:
Al Viro's avatar
Al Viro committed
523
	current->signal->rlim[RLIMIT_FSIZE].rlim_cur = flim;
524
	revert_creds(orig_cred);
Linus Torvalds's avatar
Linus Torvalds committed
525 526
}

527 528
/**
 * acct_collect - collect accounting information into pacct_struct
529 530
 * @exitcode: task exit code
 * @group_dead: not 0, if this thread is the last one in the process.
531
 */
532
void acct_collect(long exitcode, int group_dead)
533 534
{
	struct pacct_struct *pacct = &current->signal->pacct;
535
	u64 utime, stime;
536 537
	unsigned long vsize = 0;

538
	if (group_dead && current->mm) {
539
		struct vm_area_struct *vma;
540

541 542 543 544 545 546 547 548 549
		down_read(&current->mm->mmap_sem);
		vma = current->mm->mmap;
		while (vma) {
			vsize += vma->vm_end - vma->vm_start;
			vma = vma->vm_next;
		}
		up_read(&current->mm->mmap_sem);
	}

550
	spin_lock_irq(&current->sighand->siglock);
551 552 553 554 555 556 557 558 559 560 561 562 563
	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;
564 565

	task_cputime(current, &utime, &stime);
566 567
	pacct->ac_utime += utime;
	pacct->ac_stime += stime;
568 569 570
	pacct->ac_minflt += current->min_flt;
	pacct->ac_majflt += current->maj_flt;
	spin_unlock_irq(&current->sighand->siglock);
571 572
}

573
static void slow_acct_process(struct pid_namespace *ns)
Linus Torvalds's avatar
Linus Torvalds committed
574
{
575
	for ( ; ns; ns = ns->parent) {
Al Viro's avatar
Al Viro committed
576
		struct bsd_acct_struct *acct = acct_get(ns);
Al Viro's avatar
Al Viro committed
577 578 579
		if (acct) {
			do_acct_process(acct);
			mutex_unlock(&acct->lock);
Al Viro's avatar
Al Viro committed
580
			acct_put(acct);
581 582
		}
	}
Linus Torvalds's avatar
Linus Torvalds committed
583
}
584 585

/**
586
 * acct_process
587 588 589 590 591 592 593
 *
 * handles process accounting for an exiting task
 */
void acct_process(void)
{
	struct pid_namespace *ns;

594 595 596 597 598
	/*
	 * This loop is safe lockless, since current is still
	 * alive and holds its namespace, which in turn holds
	 * its parent.
	 */
599
	for (ns = task_active_pid_ns(current); ns != NULL; ns = ns->parent) {
Al Viro's avatar
Al Viro committed
600
		if (ns->bacct)
601 602 603 604
			break;
	}
	if (unlikely(ns))
		slow_acct_process(ns);
605
}