--- //depot/projects/smpng/sys/kern/kern_acct.c 2005/11/21 20:05:51 +++ //depot/user/jhb/lock/kern/kern_acct.c 2006/02/03 22:41:00 @@ -48,23 +48,25 @@ #include #include +#include +#include +#include +#include #include +#include +#include #include -#include +#include #include -#include -#include -#include -#include -#include -#include +#include +#include #include +#include #include -#include -#include -#include -#include +#include +#include #include +#include /* * The routines implemented in this file are described in: @@ -83,12 +85,9 @@ * was provided by UCB with the 4.4BSD-Lite release */ static comp_t encode_comp_t(u_long, u_long); -static void acctwatch(void *); - -/* - * Accounting callout used for periodic scheduling of acctwatch. - */ -static struct callout acctwatch_callout; +static void acctwatch(void); +static void acct_thread(void *); +static int acct_disable(struct thread *); /* * Accounting vnode pointer, saved vnode pointer, and flags for each. @@ -104,6 +103,14 @@ SX_SYSINIT(acct, &acct_sx, "acct_sx"); /* + * State of the accounting kthread. + */ +static int acct_state; + +#define ACCT_RUNNING 1 /* Accounting kthread is running. */ +#define ACCT_EXITREQ 2 /* Accounting kthread should exit. */ + +/* * Values associated with enabling and disabling accounting */ static int acctsuspend = 2; /* stop accounting when < 2% free space left */ @@ -143,7 +150,7 @@ * appending and make sure it's a 'normal'. While we could * conditionally acquire Giant here, we're actually interacting with * vnodes from possibly two file systems, making the logic a bit - * complicated. For now, use Giant unconditionally. + * complicated. For now, use Giant unconditionally. */ mtx_lock(&Giant); if (uap->path != NULL) { @@ -188,16 +195,13 @@ * enabled. */ acct_suspended = 0; - if (acct_vp != NULL) { - callout_stop(&acctwatch_callout); - error = vn_close(acct_vp, acct_flags, acct_cred, td); - crfree(acct_cred); - acct_vp = NULL; - acct_cred = NULL; - acct_flags = 0; - log(LOG_NOTICE, "Accounting disabled\n"); - } + if (acct_vp != NULL) + error = acct_disable(td); if (uap->path == NULL) { + if (acct_state & ACCT_RUNNING) { + acct_state |= ACCT_EXITREQ; + wakeup(&acct_state); + } sx_xunlock(&acct_sx); goto done; } @@ -209,15 +213,49 @@ acct_vp = nd.ni_vp; acct_cred = crhold(td->td_ucred); acct_flags = flags; - callout_init(&acctwatch_callout, CALLOUT_MPSAFE); + if (acct_state & ACCT_RUNNING) + acct_state &= ~ACCT_EXITREQ; + else { + /* + * Try to start up an accounting thread. We may start more + * than one, but if so the extras will commit suicide as + * soon as they start up. + */ + error = kthread_create(acct_thread, NULL, NULL, 0, 0, + "accounting"); + if (error) { + (void) vn_close(acct_vp, acct_flags, acct_cred, td); + crfree(acct_cred); + acct_vp = NULL; + acct_cred = NULL; + acct_flags = 0; + sx_xunlock(&acct_sx); + log(LOG_NOTICE, "Unable to start accounting thread\n"); + goto done; + } + } sx_xunlock(&acct_sx); log(LOG_NOTICE, "Accounting enabled\n"); - acctwatch(NULL); done: mtx_unlock(&Giant); return (error); } +static int +acct_disable(struct thread *td) +{ + int error; + + sx_assert(&acct_sx, SX_XLOCKED); + error = vn_close(acct_vp, acct_flags, acct_cred, td); + crfree(acct_cred); + acct_vp = NULL; + acct_cred = NULL; + acct_flags = 0; + log(LOG_NOTICE, "Accounting disabled\n"); + return (error); +} + /* * Write out process accounting information, on process exit. * Data to be written out is specified in Leffler, et al. @@ -303,19 +341,21 @@ /* (8) The boolean flags that tell how the process terminated, etc. */ acct.ac_flag = p->p_acflag; - PROC_UNLOCK(p); /* * Eliminate any file size rlimit. */ - newlim = lim_alloc(); - PROC_LOCK(p); - oldlim = p->p_limit; - lim_copy(newlim, oldlim); - newlim->pl_rlimit[RLIMIT_FSIZE].rlim_cur = RLIM_INFINITY; - p->p_limit = newlim; + if (p->p_limit->pl_rlimit[RLIMIT_FSIZE].rlim_cur != RLIM_INFINITY) { + PROC_UNLOCK(p); + newlim = lim_alloc(); + PROC_LOCK(p); + oldlim = p->p_limit; + lim_copy(newlim, oldlim); + newlim->pl_rlimit[RLIMIT_FSIZE].rlim_cur = RLIM_INFINITY; + p->p_limit = newlim; + lim_free(oldlim); + } PROC_UNLOCK(p); - lim_free(oldlim); /* * Write the accounting information to the file. @@ -376,31 +416,41 @@ */ /* ARGSUSED */ static void -acctwatch(void *a) +acctwatch(void) { struct statfs sb; int vfslocked; - sx_xlock(&acct_sx); + sx_assert(&acct_sx, SX_XLOCKED); + + /* + * If accounting was disabled before our kthread was scheduled, + * then acct_vp might be NULL. If so, just ask our kthread to + * exit and return. + */ + if (acct_vp == NULL) { + acct_state |= ACCT_EXITREQ; + return; + } + + /* + * If our vnode is no longer valid, tear it down and signal the + * accounting thread to die. + */ vfslocked = VFS_LOCK_GIANT(acct_vp->v_mount); if (acct_vp->v_type == VBAD) { - (void) vn_close(acct_vp, acct_flags, acct_cred, NULL); + (void) acct_disable(NULL); VFS_UNLOCK_GIANT(vfslocked); - crfree(acct_cred); - acct_vp = NULL; - acct_cred = NULL; - acct_flags = 0; - sx_xunlock(&acct_sx); - log(LOG_NOTICE, "Accounting disabled\n"); + acct_state |= ACCT_EXITREQ; return; } + /* * Stopping here is better than continuing, maybe it will be VBAD * next time around. */ if (VFS_STATFS(acct_vp->v_mount, &sb, curthread) < 0) { VFS_UNLOCK_GIANT(vfslocked); - sx_xunlock(&acct_sx); return; } VFS_UNLOCK_GIANT(vfslocked); @@ -417,6 +467,54 @@ log(LOG_NOTICE, "Accounting suspended\n"); } } - callout_reset(&acctwatch_callout, acctchkfreq * hz, acctwatch, NULL); +} + +/* + * The main loop for the dedicated kernel thread to manage periodically + * call acctwatch(). + */ +static void +acct_thread(void *dummy) +{ + u_char pri; + + /* This is a low-priority kernel thread. */ + pri = PRI_MAX_KERN; + mtx_lock_spin(&sched_lock); + sched_prio(curthread, pri); + mtx_unlock_spin(&sched_lock); + + /* If another accounting kthread is already running, just die. */ + sx_xlock(&acct_sx); + if (acct_state & ACCT_RUNNING) { + sx_xunlock(&acct_sx); + kthread_exit(0); + } + acct_state |= ACCT_RUNNING; + + /* Loop until we are asked to exit. */ + while (!(acct_state & ACCT_EXITREQ)) { + + /* Perform our periodic checks. */ + acctwatch(); + + /* + * We check this flag again before sleeping since the + * acctwatch() might have shut down accounting and asked us + * to exit. + */ + if (!(acct_state & ACCT_EXITREQ)) { + sx_xunlock(&acct_sx); + tsleep(&acct_state, pri, "-", acctchkfreq * hz); + sx_xlock(&acct_sx); + } + } + + /* + * Acknowledge the exit request and shutdown. We clear both the + * exit request and running flags. + */ + acct_state = 0; sx_xunlock(&acct_sx); + kthread_exit(0); }