- Add a kthread to periodically call acctwatch() when accounting is active
instead of calling acctwatch() from softclock. The acctwatch() function needs to hold an sx lock and also makes a VFS call, and neither of these are good things (or safe) to do from a callout. The kthread only exists and is running when accounting is turned on; it is started and stopped as needed. I didn't run acctwatch() via the thread taskqueue at Robert's request as he was worried that if the accounting file was over NFS the VFS_STAT() calls might stall other work on the taskqueue. - Add an acct_disable() function to take care of closing the accounting vnode and cleaning up so we don't duplicate the same code in two different places. MFC after: 3 days
This commit is contained in:
parent
74b07c5412
commit
505a14934e
@ -51,6 +51,7 @@ __FBSDID("$FreeBSD$");
|
||||
#include <sys/acct.h>
|
||||
#include <sys/fcntl.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/kthread.h>
|
||||
#include <sys/lock.h>
|
||||
#include <sys/mac.h>
|
||||
#include <sys/mount.h>
|
||||
@ -58,6 +59,7 @@ __FBSDID("$FreeBSD$");
|
||||
#include <sys/namei.h>
|
||||
#include <sys/proc.h>
|
||||
#include <sys/resourcevar.h>
|
||||
#include <sys/sched.h>
|
||||
#include <sys/sx.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/sysent.h>
|
||||
@ -83,12 +85,9 @@ __FBSDID("$FreeBSD$");
|
||||
* 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.
|
||||
@ -103,6 +102,14 @@ static struct sx acct_sx;
|
||||
|
||||
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
|
||||
*/
|
||||
@ -188,16 +195,13 @@ acct(struct thread *td, struct acct_args *uap)
|
||||
* 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,53 @@ acct(struct thread *td, struct acct_args *uap)
|
||||
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 kthread. 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);
|
||||
}
|
||||
|
||||
/*
|
||||
* Disable currently in-progress accounting by closing the vnode, dropping
|
||||
* our reference to the credential, and clearing the vnode's flags.
|
||||
*/
|
||||
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.
|
||||
@ -376,31 +418,41 @@ encode_comp_t(u_long s, u_long us)
|
||||
*/
|
||||
/* ARGSUSED */
|
||||
static void
|
||||
acctwatch(void *a)
|
||||
acctwatch(void)
|
||||
{
|
||||
struct statfs sb;
|
||||
int vfslocked;
|
||||
|
||||
sx_xlock(&acct_sx);
|
||||
vfslocked = VFS_LOCK_GIANT(acct_vp->v_mount);
|
||||
if (acct_vp->v_type == VBAD) {
|
||||
(void) vn_close(acct_vp, acct_flags, acct_cred, 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");
|
||||
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) acct_disable(NULL);
|
||||
VFS_UNLOCK_GIANT(vfslocked);
|
||||
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 +469,54 @@ acctwatch(void *a)
|
||||
log(LOG_NOTICE, "Accounting suspended\n");
|
||||
}
|
||||
}
|
||||
callout_reset(&acctwatch_callout, acctchkfreq * hz, acctwatch, NULL);
|
||||
sx_xunlock(&acct_sx);
|
||||
}
|
||||
|
||||
/*
|
||||
* The main loop for the dedicated kernel thread that periodically calls
|
||||
* 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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user