Index: head/share/man/man9/alq.9 =================================================================== --- head/share/man/man9/alq.9 (revision 145141) +++ head/share/man/man9/alq.9 (revision 145142) @@ -1,243 +1,256 @@ .\" .\" Copyright (c) 2003 Hiten Pandya .\" All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions, and the following disclaimer, .\" without modification, immediately at the beginning of the file. .\" 2. The name of the author may not be used to endorse or promote products .\" derived from this software without specific prior written permission. .\" .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE .\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR .\" ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" .\" $FreeBSD$ .\" .Dd May 16, 2003 .Dt ALQ 9 .Os .Sh NAME .Nm alq , .Nm alq_open , .Nm alq_write , .Nm alq_flush , .Nm alq_close , .Nm alq_get , .Nm alq_post .Nd Asynchronous Logging Queues .Sh SYNOPSIS .In sys/alq.h .Ft int .Fo alq_open .Fa "struct alq **app" .Fa "const char *file" .Fa "struct ucred *cred" +.Fa "int cmode" .Fa "int size" .Fa "int count" .Fc .Ft int .Fn alq_write "struct alq *alq" "void *data" "int waitok" .Ft void .Fn alq_flush "struct alq *alq" .Ft void .Fn alq_close "struct alq *alq" .Ft struct ale * .Fn alq_get "struct alq *alq" "int waitok" .Ft void .Fn alq_post "struct alq *alq" "struct ale *ale" .Sh DESCRIPTION The .Nm facility provides an asynchronous fixed length recording mechanism, known as Asynchronous Logging Queues. It can record to any .Xr vnode 9 , thus providing the ability to journal logs to character devices as well as regular files. All functions accept a .Vt "struct alq" argument, which is an opaque type that maintains state information for an Asynchronous Logging Queue. The logging facility runs in a separate kernel thread, which services all log entry requests. .Pp An .Dq asynchronous log entry is defined as .Vt "struct ale" , which has the following members: .Bd -literal -offset indent struct ale { struct ale *ae_next; /* Next Entry */ char *ae_data; /* Entry buffer */ int ae_flags; /* Entry flags */ }; .Ed .Pp The .Va ae_flags field is for internal use, clients of the .Nm interface should not modify this field. Behaviour is undefined if this field is modified. .Sh FUNCTIONS The .Fn alq_open function creates a new logging queue. The .Fa file -argument is the name of the file to open for logging. +argument is the name of the file to open for logging; if the file does not +yet exist, +.Fn alq_open +will attempt to create it. +The +.Fa cmode +argument will be passed to +.Fn vn_open +as the requested creation mode, to be used if the file will be created by +.Fn alq_open . +Consumers of this API may wish to pass +.Dv ALQ_DEFAULT_CMODE , +a default creation mode suitable for most applications. The argument .Fa cred -specifies the credentials to use when opening the file. +specifies the credentials to use when opening and performing I/O on the file. The size of each entry in the queue is determined by .Fa size . The .Fa count argument determines the number of items to be stored in the asynchronous queue over an approximate period of a disk write operation. .Pp The .Fn alq_write function writes .Fa data to the designated queue, .Fa alq . In the event that .Fn alq_write could not write the entry immediately, and .Dv ALQ_WAITOK is passed to .Fa waitok , then .Fn alq_write will be allowed to .Xr tsleep 9 . .Pp The .Fn alq_flush function is used for flushing .Fa alq to the log medium that was passed to .Fn alq_open . .Pp The .Fn alq_close function will close the asynchronous logging queue, .Fa alq , and flush all pending write requests to the log medium. It will free all resources that were previously allocated. .Pp The .Fn alq_get function returns the next available asynchronous logging entry from the queue, .Fa alq . This function leaves the queue in a locked state, until a subsequent .Fn alq_post call is made. In the event that .Fn alq_get could not retrieve an entry immediately, it will .Xr tsleep 9 with the .Dq Li alqget wait message. .Pp The .Fn alq_post function schedules the asynchronous logging entry, .Fa ale , which is retrieved using the .Fn alq_get function, for writing to the asynchronous logging queue, .Fa alq . This function leaves the queue, .Fa alq , in an unlocked state. .Sh IMPLEMENTATION NOTES The .Fn alq_write function is a wrapper around the .Fn alq_get and .Fn alq_post functions; by using these functions separately, a call to .Fn bcopy can be avoided for performance critical code paths. .Sh LOCKING Each asynchronous queue is protected by a spin mutex. .Pp Functions .Fn alq_flush , .Fn alq_open and .Fn alq_post may attempt to acquire an internal sleep mutex, and should consequently not be used in contexts where sleeping is not allowed. .Sh RETURN VALUES The .Fn alq_open function returns one of the error codes listed in .Xr open 2 , if it fails to open .Fa file , or else it returns 0. .Pp The .Fn alq_write function returns .Er EWOULDBLOCK if .Dv ALQ_NOWAIT was provided as a value to .Fa waitok and either the queue is full, or when the system is shutting down. .Pp The .Fn alq_get function returns .Dv NULL , if .Dv ALQ_NOWAIT was provided as a value to .Fa waitok and either the queue is full, or when the system is shutting down. .Pp NOTE: invalid arguments to non-void functions will result in undefined behaviour. .Sh SEE ALSO .Xr syslog 3 , .Xr kthread 9 , .Xr ktr 9 , .Xr tsleep 9 , .Xr vnode 9 .Sh HISTORY The Asynchronous Logging Queues (ALQ) facility first appeared in .Fx 5.0 . .Sh AUTHORS .An -nosplit The .Nm facility was written by .An Jeffrey Roberson Aq jeff@FreeBSD.org . .Pp This manual page was written by .An Hiten Pandya Aq hmp@FreeBSD.org . Index: head/sys/contrib/dev/ath/freebsd/ah_osdep.c =================================================================== --- head/sys/contrib/dev/ath/freebsd/ah_osdep.c (revision 145141) +++ head/sys/contrib/dev/ath/freebsd/ah_osdep.c (revision 145142) @@ -1,415 +1,415 @@ /*- * Copyright (c) 2002-2004 Sam Leffler, Errno Consulting, Atheros * Communications, Inc. All rights reserved. * * Redistribution and use in source and binary forms are permitted * provided that the following conditions are met: * 1. The materials contained herein are unmodified and are used * unmodified. * 2. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following NO * ''WARRANTY'' disclaimer below (''Disclaimer''), without * modification. * 3. Redistributions in binary form must reproduce at minimum a * disclaimer similar to the Disclaimer below and any redistribution * must be conditioned upon including a substantially similar * Disclaimer requirement for further binary redistribution. * 4. Neither the names of the above-listed copyright holders nor the * names of any contributors may be used to endorse or promote * product derived from this software without specific prior written * permission. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, * MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE * FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGES. * * $Id: ah_osdep.c,v 1.35 2004/10/25 01:02:50 sam Exp $ */ #include "opt_ah.h" #include #include #include #include #include #include #include #include #include #include /* XXX for ether_sprintf */ #include extern void ath_hal_printf(struct ath_hal *, const char*, ...) __printflike(2,3); extern void ath_hal_vprintf(struct ath_hal *, const char*, __va_list) __printflike(2, 0); extern const char* ath_hal_ether_sprintf(const u_int8_t *mac); extern void *ath_hal_malloc(size_t); extern void ath_hal_free(void *); #ifdef AH_ASSERT extern void ath_hal_assert_failed(const char* filename, int lineno, const char* msg); #endif #ifdef AH_DEBUG extern void HALDEBUG(struct ath_hal *ah, const char* fmt, ...); extern void HALDEBUGn(struct ath_hal *ah, u_int level, const char* fmt, ...); #endif /* AH_DEBUG */ /* NB: put this here instead of the driver to avoid circular references */ SYSCTL_NODE(_hw, OID_AUTO, ath, CTLFLAG_RD, 0, "Atheros driver parameters"); SYSCTL_NODE(_hw_ath, OID_AUTO, hal, CTLFLAG_RD, 0, "Atheros HAL parameters"); #ifdef AH_DEBUG static int ath_hal_debug = 0; SYSCTL_INT(_hw_ath_hal, OID_AUTO, debug, CTLFLAG_RW, &ath_hal_debug, 0, "Atheros HAL debugging printfs"); TUNABLE_INT("hw.ath.hal.debug", &ath_hal_debug); #endif /* AH_DEBUG */ SYSCTL_STRING(_hw_ath_hal, OID_AUTO, version, CTLFLAG_RD, ath_hal_version, 0, "Atheros HAL version"); int ath_hal_dma_beacon_response_time = 2; /* in TU's */ SYSCTL_INT(_hw_ath_hal, OID_AUTO, dma_brt, CTLFLAG_RW, &ath_hal_dma_beacon_response_time, 0, "Atheros HAL DMA beacon response time"); int ath_hal_sw_beacon_response_time = 10; /* in TU's */ SYSCTL_INT(_hw_ath_hal, OID_AUTO, sw_brt, CTLFLAG_RW, &ath_hal_sw_beacon_response_time, 0, "Atheros HAL software beacon response time"); int ath_hal_additional_swba_backoff = 0; /* in TU's */ SYSCTL_INT(_hw_ath_hal, OID_AUTO, swba_backoff, CTLFLAG_RW, &ath_hal_additional_swba_backoff, 0, "Atheros HAL additional SWBA backoff time"); void* ath_hal_malloc(size_t size) { return malloc(size, M_DEVBUF, M_NOWAIT | M_ZERO); } void ath_hal_free(void* p) { return free(p, M_DEVBUF); } void ath_hal_vprintf(struct ath_hal *ah, const char* fmt, va_list ap) { vprintf(fmt, ap); } void ath_hal_printf(struct ath_hal *ah, const char* fmt, ...) { va_list ap; va_start(ap, fmt); ath_hal_vprintf(ah, fmt, ap); va_end(ap); } const char* ath_hal_ether_sprintf(const u_int8_t *mac) { return ether_sprintf(mac); } #ifdef AH_DEBUG void HALDEBUG(struct ath_hal *ah, const char* fmt, ...) { if (ath_hal_debug) { __va_list ap; va_start(ap, fmt); ath_hal_vprintf(ah, fmt, ap); va_end(ap); } } void HALDEBUGn(struct ath_hal *ah, u_int level, const char* fmt, ...) { if (ath_hal_debug >= level) { __va_list ap; va_start(ap, fmt); ath_hal_vprintf(ah, fmt, ap); va_end(ap); } } #endif /* AH_DEBUG */ #ifdef AH_DEBUG_ALQ /* * ALQ register tracing support. * * Setting hw.ath.hal.alq=1 enables tracing of all register reads and * writes to the file /tmp/ath_hal.log. The file format is a simple * fixed-size array of records. When done logging set hw.ath.hal.alq=0 * and then decode the file with the arcode program (that is part of the * HAL). If you start+stop tracing the data will be appended to an * existing file. * * NB: doesn't handle multiple devices properly; only one DEVICE record * is emitted and the different devices are not identified. */ #include #include #include static struct alq *ath_hal_alq; static int ath_hal_alq_emitdev; /* need to emit DEVICE record */ static u_int ath_hal_alq_lost; /* count of lost records */ static const char *ath_hal_logfile = "/tmp/ath_hal.log"; static u_int ath_hal_alq_qsize = 64*1024; static int ath_hal_setlogging(int enable) { int error; if (enable) { error = suser(curthread); if (error == 0) { error = alq_open(&ath_hal_alq, ath_hal_logfile, - curthread->td_ucred, + curthread->td_ucred, ALQ_DEFAULT_CMODE, sizeof (struct athregrec), ath_hal_alq_qsize); ath_hal_alq_lost = 0; ath_hal_alq_emitdev = 1; printf("ath_hal: logging to %s enabled\n", ath_hal_logfile); } } else { if (ath_hal_alq) alq_close(ath_hal_alq); ath_hal_alq = NULL; printf("ath_hal: logging disabled\n"); error = 0; } return (error); } static int sysctl_hw_ath_hal_log(SYSCTL_HANDLER_ARGS) { int error, enable; enable = (ath_hal_alq != NULL); error = sysctl_handle_int(oidp, &enable, 0, req); if (error || !req->newptr) return (error); else return (ath_hal_setlogging(enable)); } SYSCTL_PROC(_hw_ath_hal, OID_AUTO, alq, CTLTYPE_INT|CTLFLAG_RW, 0, 0, sysctl_hw_ath_hal_log, "I", "Enable HAL register logging"); SYSCTL_INT(_hw_ath_hal, OID_AUTO, alq_size, CTLFLAG_RW, &ath_hal_alq_qsize, 0, "In-memory log size (#records)"); SYSCTL_INT(_hw_ath_hal, OID_AUTO, alq_lost, CTLFLAG_RW, &ath_hal_alq_lost, 0, "Register operations not logged"); static struct ale * ath_hal_alq_get(struct ath_hal *ah) { struct ale *ale; if (ath_hal_alq_emitdev) { ale = alq_get(ath_hal_alq, ALQ_NOWAIT); if (ale) { struct athregrec *r = (struct athregrec *) ale->ae_data; r->op = OP_DEVICE; r->reg = 0; r->val = ah->ah_devid; alq_post(ath_hal_alq, ale); ath_hal_alq_emitdev = 0; } else ath_hal_alq_lost++; } ale = alq_get(ath_hal_alq, ALQ_NOWAIT); if (!ale) ath_hal_alq_lost++; return ale; } void ath_hal_reg_write(struct ath_hal *ah, u_int32_t reg, u_int32_t val) { if (ath_hal_alq) { struct ale *ale = ath_hal_alq_get(ah); if (ale) { struct athregrec *r = (struct athregrec *) ale->ae_data; r->op = OP_WRITE; r->reg = reg; r->val = val; alq_post(ath_hal_alq, ale); } } #if _BYTE_ORDER == _BIG_ENDIAN if (reg >= 0x4000 && reg < 0x5000) bus_space_write_4(ah->ah_st, ah->ah_sh, reg, htole32(val)); else #endif bus_space_write_4(ah->ah_st, ah->ah_sh, reg, val); } u_int32_t ath_hal_reg_read(struct ath_hal *ah, u_int32_t reg) { u_int32_t val; val = bus_space_read_4(ah->ah_st, ah->ah_sh, reg); #if _BYTE_ORDER == _BIG_ENDIAN if (reg >= 0x4000 && reg < 0x5000) val = le32toh(val); #endif if (ath_hal_alq) { struct ale *ale = ath_hal_alq_get(ah); if (ale) { struct athregrec *r = (struct athregrec *) ale->ae_data; r->op = OP_READ; r->reg = reg; r->val = val; alq_post(ath_hal_alq, ale); } } return val; } void OS_MARK(struct ath_hal *ah, u_int id, u_int32_t v) { if (ath_hal_alq) { struct ale *ale = ath_hal_alq_get(ah); if (ale) { struct athregrec *r = (struct athregrec *) ale->ae_data; r->op = OP_MARK; r->reg = id; r->val = v; alq_post(ath_hal_alq, ale); } } } #elif defined(AH_DEBUG) || defined(AH_REGOPS_FUNC) /* * Memory-mapped device register read/write. These are here * as routines when debugging support is enabled and/or when * explicitly configured to use function calls. The latter is * for architectures that might need to do something before * referencing memory (e.g. remap an i/o window). * * NB: see the comments in ah_osdep.h about byte-swapping register * reads and writes to understand what's going on below. */ void ath_hal_reg_write(struct ath_hal *ah, u_int32_t reg, u_int32_t val) { #if _BYTE_ORDER == _BIG_ENDIAN if (reg >= 0x4000 && reg < 0x5000) bus_space_write_4(ah->ah_st, ah->ah_sh, reg, htole32(val)); else #endif bus_space_write_4(ah->ah_st, ah->ah_sh, reg, val); } u_int32_t ath_hal_reg_read(struct ath_hal *ah, u_int32_t reg) { u_int32_t val; val = bus_space_read_4(ah->ah_st, ah->ah_sh, reg); #if _BYTE_ORDER == _BIG_ENDIAN if (reg >= 0x4000 && reg < 0x5000) val = le32toh(val); #endif return val; } #endif /* AH_DEBUG || AH_REGOPS_FUNC */ #ifdef AH_ASSERT void ath_hal_assert_failed(const char* filename, int lineno, const char *msg) { printf("Atheros HAL assertion failure: %s: line %u: %s\n", filename, lineno, msg); panic("ath_hal_assert"); } #endif /* AH_ASSERT */ /* * Delay n microseconds. */ void ath_hal_delay(int n) { DELAY(n); } u_int32_t ath_hal_getuptime(struct ath_hal *ah) { struct bintime bt; getbinuptime(&bt); return (bt.sec * 1000) + (((uint64_t)1000 * (uint32_t)(bt.frac >> 32)) >> 32); } void ath_hal_memzero(void *dst, size_t n) { bzero(dst, n); } void * ath_hal_memcpy(void *dst, const void *src, size_t n) { return memcpy(dst, src, n); } /* * Module glue. */ static int ath_hal_modevent(module_t mod, int type, void *unused) { const char *sep; int i; switch (type) { case MOD_LOAD: printf("ath_hal: %s (", ath_hal_version); sep = ""; for (i = 0; ath_hal_buildopts[i] != NULL; i++) { printf("%s%s", sep, ath_hal_buildopts[i]); sep = ", "; } printf(")\n"); return 0; case MOD_UNLOAD: return 0; } return EINVAL; } static moduledata_t ath_hal_mod = { "ath_hal", ath_hal_modevent, 0 }; DECLARE_MODULE(ath_hal, ath_hal_mod, SI_SUB_DRIVERS, SI_ORDER_ANY); MODULE_VERSION(ath_hal, 1); Index: head/sys/kern/kern_alq.c =================================================================== --- head/sys/kern/kern_alq.c (revision 145141) +++ head/sys/kern/kern_alq.c (revision 145142) @@ -1,514 +1,514 @@ /*- * Copyright (c) 2002, Jeffrey Roberson * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice unmodified, this list of conditions, and the following * disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include "opt_mac.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* Async. Logging Queue */ struct alq { int aq_entmax; /* Max entries */ int aq_entlen; /* Entry length */ char *aq_entbuf; /* Buffer for stored entries */ int aq_flags; /* Queue flags */ struct mtx aq_mtx; /* Queue lock */ struct vnode *aq_vp; /* Open vnode handle */ struct ucred *aq_cred; /* Credentials of the opening thread */ struct ale *aq_first; /* First ent */ struct ale *aq_entfree; /* First free ent */ struct ale *aq_entvalid; /* First ent valid for writing */ LIST_ENTRY(alq) aq_act; /* List of active queues */ LIST_ENTRY(alq) aq_link; /* List of all queues */ }; #define AQ_WANTED 0x0001 /* Wakeup sleeper when io is done */ #define AQ_ACTIVE 0x0002 /* on the active list */ #define AQ_FLUSHING 0x0004 /* doing IO */ #define AQ_SHUTDOWN 0x0008 /* Queue no longer valid */ #define ALQ_LOCK(alq) mtx_lock_spin(&(alq)->aq_mtx) #define ALQ_UNLOCK(alq) mtx_unlock_spin(&(alq)->aq_mtx) static MALLOC_DEFINE(M_ALD, "ALD", "ALD"); /* * The ald_mtx protects the ald_queues list and the ald_active list. */ static struct mtx ald_mtx; static LIST_HEAD(, alq) ald_queues; static LIST_HEAD(, alq) ald_active; static int ald_shutingdown = 0; struct thread *ald_thread; static struct proc *ald_proc; #define ALD_LOCK() mtx_lock(&ald_mtx) #define ALD_UNLOCK() mtx_unlock(&ald_mtx) /* Daemon functions */ static int ald_add(struct alq *); static int ald_rem(struct alq *); static void ald_startup(void *); static void ald_daemon(void); static void ald_shutdown(void *, int); static void ald_activate(struct alq *); static void ald_deactivate(struct alq *); /* Internal queue functions */ static void alq_shutdown(struct alq *); static int alq_doio(struct alq *); /* * Add a new queue to the global list. Fail if we're shutting down. */ static int ald_add(struct alq *alq) { int error; error = 0; ALD_LOCK(); if (ald_shutingdown) { error = EBUSY; goto done; } LIST_INSERT_HEAD(&ald_queues, alq, aq_link); done: ALD_UNLOCK(); return (error); } /* * Remove a queue from the global list unless we're shutting down. If so, * the ald will take care of cleaning up it's resources. */ static int ald_rem(struct alq *alq) { int error; error = 0; ALD_LOCK(); if (ald_shutingdown) { error = EBUSY; goto done; } LIST_REMOVE(alq, aq_link); done: ALD_UNLOCK(); return (error); } /* * Put a queue on the active list. This will schedule it for writing. */ static void ald_activate(struct alq *alq) { LIST_INSERT_HEAD(&ald_active, alq, aq_act); wakeup(&ald_active); } static void ald_deactivate(struct alq *alq) { LIST_REMOVE(alq, aq_act); alq->aq_flags &= ~AQ_ACTIVE; } static void ald_startup(void *unused) { mtx_init(&ald_mtx, "ALDmtx", NULL, MTX_DEF|MTX_QUIET); LIST_INIT(&ald_queues); LIST_INIT(&ald_active); } static void ald_daemon(void) { int needwakeup; struct alq *alq; mtx_lock(&Giant); ald_thread = FIRST_THREAD_IN_PROC(ald_proc); EVENTHANDLER_REGISTER(shutdown_pre_sync, ald_shutdown, NULL, SHUTDOWN_PRI_FIRST); ALD_LOCK(); for (;;) { while ((alq = LIST_FIRST(&ald_active)) == NULL) msleep(&ald_active, &ald_mtx, PWAIT, "aldslp", 0); ALQ_LOCK(alq); ald_deactivate(alq); ALD_UNLOCK(); needwakeup = alq_doio(alq); ALQ_UNLOCK(alq); if (needwakeup) wakeup(alq); ALD_LOCK(); } } static void ald_shutdown(void *arg, int howto) { struct alq *alq; ALD_LOCK(); ald_shutingdown = 1; while ((alq = LIST_FIRST(&ald_queues)) != NULL) { LIST_REMOVE(alq, aq_link); ALD_UNLOCK(); alq_shutdown(alq); ALD_LOCK(); } ALD_UNLOCK(); } static void alq_shutdown(struct alq *alq) { ALQ_LOCK(alq); /* Stop any new writers. */ alq->aq_flags |= AQ_SHUTDOWN; /* Drain IO */ while (alq->aq_flags & (AQ_FLUSHING|AQ_ACTIVE)) { alq->aq_flags |= AQ_WANTED; ALQ_UNLOCK(alq); tsleep(alq, PWAIT, "aldclose", 0); ALQ_LOCK(alq); } ALQ_UNLOCK(alq); vn_close(alq->aq_vp, FWRITE, alq->aq_cred, curthread); crfree(alq->aq_cred); } /* * Flush all pending data to disk. This operation will block. */ static int alq_doio(struct alq *alq) { struct thread *td; struct mount *mp; struct vnode *vp; struct uio auio; struct iovec aiov[2]; struct ale *ale; struct ale *alstart; int totlen; int iov; vp = alq->aq_vp; td = curthread; totlen = 0; iov = 0; alstart = ale = alq->aq_entvalid; alq->aq_entvalid = NULL; bzero(&aiov, sizeof(aiov)); bzero(&auio, sizeof(auio)); do { if (aiov[iov].iov_base == NULL) aiov[iov].iov_base = ale->ae_data; aiov[iov].iov_len += alq->aq_entlen; totlen += alq->aq_entlen; /* Check to see if we're wrapping the buffer */ if (ale->ae_data + alq->aq_entlen != ale->ae_next->ae_data) iov++; ale->ae_flags &= ~AE_VALID; ale = ale->ae_next; } while (ale->ae_flags & AE_VALID); alq->aq_flags |= AQ_FLUSHING; ALQ_UNLOCK(alq); if (iov == 2 || aiov[iov].iov_base == NULL) iov--; auio.uio_iov = &aiov[0]; auio.uio_offset = 0; auio.uio_segflg = UIO_SYSSPACE; auio.uio_rw = UIO_WRITE; auio.uio_iovcnt = iov + 1; auio.uio_resid = totlen; auio.uio_td = td; /* * Do all of the junk required to write now. */ vn_start_write(vp, &mp, V_WAIT); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); VOP_LEASE(vp, td, alq->aq_cred, LEASE_WRITE); /* * XXX: VOP_WRITE error checks are ignored. */ #ifdef MAC if (mac_check_vnode_write(alq->aq_cred, NOCRED, vp) == 0) #endif VOP_WRITE(vp, &auio, IO_UNIT | IO_APPEND, alq->aq_cred); VOP_UNLOCK(vp, 0, td); vn_finished_write(mp); ALQ_LOCK(alq); alq->aq_flags &= ~AQ_FLUSHING; if (alq->aq_entfree == NULL) alq->aq_entfree = alstart; if (alq->aq_flags & AQ_WANTED) { alq->aq_flags &= ~AQ_WANTED; return (1); } return(0); } static struct kproc_desc ald_kp = { "ALQ Daemon", ald_daemon, &ald_proc }; SYSINIT(aldthread, SI_SUB_KTHREAD_IDLE, SI_ORDER_ANY, kproc_start, &ald_kp) SYSINIT(ald, SI_SUB_LOCK, SI_ORDER_ANY, ald_startup, NULL) /* User visible queue functions */ /* * Create the queue data structure, allocate the buffer, and open the file. */ int -alq_open(struct alq **alqp, const char *file, struct ucred *cred, int size, - int count) +alq_open(struct alq **alqp, const char *file, struct ucred *cred, int cmode, + int size, int count) { struct thread *td; struct nameidata nd; struct ale *ale; struct ale *alp; struct alq *alq; char *bufp; int flags; int error; int i; *alqp = NULL; td = curthread; NDINIT(&nd, LOOKUP, NOFOLLOW, UIO_SYSSPACE, file, td); flags = FWRITE | O_NOFOLLOW | O_CREAT; - error = vn_open_cred(&nd, &flags, 0, cred, -1); + error = vn_open_cred(&nd, &flags, cmode, cred, -1); if (error) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); /* We just unlock so we hold a reference */ VOP_UNLOCK(nd.ni_vp, 0, td); alq = malloc(sizeof(*alq), M_ALD, M_WAITOK|M_ZERO); alq->aq_entbuf = malloc(count * size, M_ALD, M_WAITOK|M_ZERO); alq->aq_first = malloc(sizeof(*ale) * count, M_ALD, M_WAITOK|M_ZERO); alq->aq_vp = nd.ni_vp; alq->aq_cred = crhold(cred); alq->aq_entmax = count; alq->aq_entlen = size; alq->aq_entfree = alq->aq_first; mtx_init(&alq->aq_mtx, "ALD Queue", NULL, MTX_SPIN|MTX_QUIET); bufp = alq->aq_entbuf; ale = alq->aq_first; alp = NULL; /* Match up entries with buffers */ for (i = 0; i < count; i++) { if (alp) alp->ae_next = ale; ale->ae_data = bufp; alp = ale; ale++; bufp += size; } alp->ae_next = alq->aq_first; if ((error = ald_add(alq)) != 0) return (error); *alqp = alq; return (0); } /* * Copy a new entry into the queue. If the operation would block either * wait or return an error depending on the value of waitok. */ int alq_write(struct alq *alq, void *data, int waitok) { struct ale *ale; if ((ale = alq_get(alq, waitok)) == NULL) return (EWOULDBLOCK); bcopy(data, ale->ae_data, alq->aq_entlen); alq_post(alq, ale); return (0); } struct ale * alq_get(struct alq *alq, int waitok) { struct ale *ale; struct ale *aln; ale = NULL; ALQ_LOCK(alq); /* Loop until we get an entry or we're shutting down */ while ((alq->aq_flags & AQ_SHUTDOWN) == 0 && (ale = alq->aq_entfree) == NULL && (waitok & ALQ_WAITOK)) { alq->aq_flags |= AQ_WANTED; ALQ_UNLOCK(alq); tsleep(alq, PWAIT, "alqget", 0); ALQ_LOCK(alq); } if (ale != NULL) { aln = ale->ae_next; if ((aln->ae_flags & AE_VALID) == 0) alq->aq_entfree = aln; else alq->aq_entfree = NULL; } else ALQ_UNLOCK(alq); return (ale); } void alq_post(struct alq *alq, struct ale *ale) { int activate; ale->ae_flags |= AE_VALID; if (alq->aq_entvalid == NULL) alq->aq_entvalid = ale; if ((alq->aq_flags & AQ_ACTIVE) == 0) { alq->aq_flags |= AQ_ACTIVE; activate = 1; } else activate = 0; ALQ_UNLOCK(alq); if (activate) { ALD_LOCK(); ald_activate(alq); ALD_UNLOCK(); } } void alq_flush(struct alq *alq) { int needwakeup = 0; ALD_LOCK(); ALQ_LOCK(alq); if (alq->aq_flags & AQ_ACTIVE) { ald_deactivate(alq); ALD_UNLOCK(); needwakeup = alq_doio(alq); } else ALD_UNLOCK(); ALQ_UNLOCK(alq); if (needwakeup) wakeup(alq); } /* * Flush remaining data, close the file and free all resources. */ void alq_close(struct alq *alq) { /* * If we're already shuting down someone else will flush and close * the vnode. */ if (ald_rem(alq) != 0) return; /* * Drain all pending IO. */ alq_shutdown(alq); mtx_destroy(&alq->aq_mtx); free(alq->aq_first, M_ALD); free(alq->aq_entbuf, M_ALD); free(alq, M_ALD); } Index: head/sys/kern/kern_ktr.c =================================================================== --- head/sys/kern/kern_ktr.c (revision 145141) +++ head/sys/kern/kern_ktr.c (revision 145142) @@ -1,330 +1,330 @@ /*- * Copyright (c) 2000 John Baldwin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * This module holds the global variables used by KTR and the ktr_tracepoint() * function that does the actual tracing. */ #include __FBSDID("$FreeBSD$"); #include "opt_ddb.h" #include "opt_ktr.h" #include "opt_alq.h" #include #include #include #include #include #include #include #include #include #include #include #ifdef __sparc64__ #include #endif #include #ifndef KTR_ENTRIES #define KTR_ENTRIES 1024 #endif #ifndef KTR_MASK #define KTR_MASK (KTR_GEN) #endif #ifndef KTR_CPUMASK #define KTR_CPUMASK (~0) #endif #ifndef KTR_TIME #define KTR_TIME get_cyclecount() #endif #ifndef KTR_CPU #define KTR_CPU PCPU_GET(cpuid) #endif SYSCTL_NODE(_debug, OID_AUTO, ktr, CTLFLAG_RD, 0, "KTR options"); int ktr_cpumask = KTR_CPUMASK; TUNABLE_INT("debug.ktr.cpumask", &ktr_cpumask); SYSCTL_INT(_debug_ktr, OID_AUTO, cpumask, CTLFLAG_RW, &ktr_cpumask, 0, ""); int ktr_mask = KTR_MASK; TUNABLE_INT("debug.ktr.mask", &ktr_mask); SYSCTL_INT(_debug_ktr, OID_AUTO, mask, CTLFLAG_RW, &ktr_mask, 0, ""); int ktr_compile = KTR_COMPILE; SYSCTL_INT(_debug_ktr, OID_AUTO, compile, CTLFLAG_RD, &ktr_compile, 0, ""); int ktr_entries = KTR_ENTRIES; SYSCTL_INT(_debug_ktr, OID_AUTO, entries, CTLFLAG_RD, &ktr_entries, 0, ""); int ktr_version = KTR_VERSION; SYSCTL_INT(_debug_ktr, OID_AUTO, version, CTLFLAG_RD, &ktr_version, 0, ""); volatile int ktr_idx = 0; struct ktr_entry ktr_buf[KTR_ENTRIES]; #ifdef KTR_VERBOSE int ktr_verbose = KTR_VERBOSE; TUNABLE_INT("debug.ktr.verbose", &ktr_verbose); SYSCTL_INT(_debug_ktr, OID_AUTO, verbose, CTLFLAG_RW, &ktr_verbose, 0, ""); #endif #ifdef KTR_ALQ struct alq *ktr_alq; char ktr_alq_file[MAXPATHLEN] = "/tmp/ktr.out"; int ktr_alq_cnt = 0; int ktr_alq_depth = KTR_ENTRIES; int ktr_alq_enabled = 0; int ktr_alq_failed = 0; int ktr_alq_max = 0; SYSCTL_INT(_debug_ktr, OID_AUTO, alq_max, CTLFLAG_RW, &ktr_alq_max, 0, "Maximum number of entries to write"); SYSCTL_INT(_debug_ktr, OID_AUTO, alq_cnt, CTLFLAG_RD, &ktr_alq_cnt, 0, "Current number of written entries"); SYSCTL_INT(_debug_ktr, OID_AUTO, alq_failed, CTLFLAG_RD, &ktr_alq_failed, 0, "Number of times we overran the buffer"); SYSCTL_INT(_debug_ktr, OID_AUTO, alq_depth, CTLFLAG_RW, &ktr_alq_depth, 0, "Number of items in the write buffer"); SYSCTL_STRING(_debug_ktr, OID_AUTO, alq_file, CTLFLAG_RW, ktr_alq_file, sizeof(ktr_alq_file), "KTR logging file"); static int sysctl_debug_ktr_alq_enable(SYSCTL_HANDLER_ARGS) { int error; int enable; enable = ktr_alq_enabled; error = sysctl_handle_int(oidp, &enable, 0, req); if (error || !req->newptr) return (error); if (enable) { if (ktr_alq_enabled) return (0); error = suser(curthread); if (error) return (error); error = alq_open(&ktr_alq, (const char *)ktr_alq_file, - req->td->td_ucred, sizeof(struct ktr_entry), - ktr_alq_depth); + req->td->td_ucred, ALQ_DEFAULT_CMODE, + sizeof(struct ktr_entry), ktr_alq_depth); if (error == 0) { ktr_mask &= ~KTR_ALQ_MASK; ktr_alq_cnt = 0; ktr_alq_failed = 0; ktr_alq_enabled = 1; } } else { if (ktr_alq_enabled == 0) return (0); ktr_alq_enabled = 0; alq_close(ktr_alq); ktr_alq = NULL; } return (error); } SYSCTL_PROC(_debug_ktr, OID_AUTO, alq_enable, CTLTYPE_INT|CTLFLAG_RW, 0, 0, sysctl_debug_ktr_alq_enable, "I", "Enable KTR logging"); #endif void ktr_tracepoint(u_int mask, const char *file, int line, const char *format, u_long arg1, u_long arg2, u_long arg3, u_long arg4, u_long arg5, u_long arg6) { struct ktr_entry *entry; #ifdef KTR_ALQ struct ale *ale = NULL; #else int newindex, saveindex; #endif #if defined(KTR_VERBOSE) || defined(KTR_ALQ) struct thread *td; #endif int cpu; if (panicstr) return; if ((ktr_mask & mask) == 0) return; cpu = KTR_CPU; if (((1 << cpu) & ktr_cpumask) == 0) return; #if defined(KTR_VERBOSE) || defined(KTR_ALQ) td = curthread; if (td->td_pflags & TDP_INKTR) return; td->td_pflags |= TDP_INKTR; #endif #ifdef KTR_ALQ if (ktr_alq_enabled && td->td_critnest == 0 && (td->td_flags & TDF_IDLETD) == 0 && td != ald_thread) { if (ktr_alq_max && ktr_alq_cnt > ktr_alq_max) goto done; if ((ale = alq_get(ktr_alq, ALQ_NOWAIT)) == NULL) { ktr_alq_failed++; goto done; } ktr_alq_cnt++; entry = (struct ktr_entry *)ale->ae_data; } else goto done; #else do { saveindex = ktr_idx; newindex = (saveindex + 1) & (KTR_ENTRIES - 1); } while (atomic_cmpset_rel_int(&ktr_idx, saveindex, newindex) == 0); entry = &ktr_buf[saveindex]; #endif entry->ktr_timestamp = KTR_TIME; entry->ktr_cpu = cpu; if (file != NULL) while (strncmp(file, "../", 3) == 0) file += 3; entry->ktr_file = file; entry->ktr_line = line; #ifdef KTR_VERBOSE if (ktr_verbose) { #ifdef SMP printf("cpu%d ", cpu); #endif if (ktr_verbose > 1) { printf("%s.%d\t", entry->ktr_file, entry->ktr_line); } printf(format, arg1, arg2, arg3, arg4, arg5, arg6); printf("\n"); } #endif entry->ktr_desc = format; entry->ktr_parms[0] = arg1; entry->ktr_parms[1] = arg2; entry->ktr_parms[2] = arg3; entry->ktr_parms[3] = arg4; entry->ktr_parms[4] = arg5; entry->ktr_parms[5] = arg6; #ifdef KTR_ALQ if (ale) alq_post(ktr_alq, ale); done: #endif #if defined(KTR_VERBOSE) || defined(KTR_ALQ) td->td_pflags &= ~TDP_INKTR; #endif } #ifdef DDB struct tstate { int cur; int first; }; static struct tstate tstate; static int db_ktr_verbose; static int db_mach_vtrace(void); DB_SHOW_COMMAND(ktr, db_ktr_all) { int quit; quit = 0; tstate.cur = (ktr_idx - 1) & (KTR_ENTRIES - 1); tstate.first = -1; if (strcmp(modif, "v") == 0) db_ktr_verbose = 1; else db_ktr_verbose = 0; if (strcmp(modif, "a") == 0) { while (cncheckc() != -1) if (db_mach_vtrace() == 0) break; } else { db_setup_paging(db_simple_pager, &quit, db_lines_per_page); while (!quit) if (db_mach_vtrace() == 0) break; } } static int db_mach_vtrace(void) { struct ktr_entry *kp; if (tstate.cur == tstate.first) { db_printf("--- End of trace buffer ---\n"); return (0); } kp = &ktr_buf[tstate.cur]; /* Skip over unused entries. */ if (kp->ktr_desc == NULL) { db_printf("--- End of trace buffer ---\n"); return (0); } db_printf("%d: ", tstate.cur); #ifdef SMP db_printf("cpu%d ", kp->ktr_cpu); #endif if (db_ktr_verbose) { db_printf("%10.10lld %s.%d\t", (long long)kp->ktr_timestamp, kp->ktr_file, kp->ktr_line); } db_printf(kp->ktr_desc, kp->ktr_parms[0], kp->ktr_parms[1], kp->ktr_parms[2], kp->ktr_parms[3], kp->ktr_parms[4], kp->ktr_parms[5]); db_printf("\n"); if (tstate.first == -1) tstate.first = tstate.cur; if (--tstate.cur < 0) tstate.cur = KTR_ENTRIES - 1; return (1); } #endif /* DDB */ Index: head/sys/sys/alq.h =================================================================== --- head/sys/sys/alq.h (revision 145141) +++ head/sys/sys/alq.h (revision 145142) @@ -1,122 +1,127 @@ /*- * Copyright (c) 2002, Jeffrey Roberson * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice unmodified, this list of conditions, and the following * disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ * */ #ifndef _SYS_ALQ_H_ #define _SYS_ALQ_H_ /* * Opaque type for the Async. Logging Queue */ struct alq; /* The thread for the logging daemon */ extern struct thread *ald_thread; /* * Async. Logging Entry */ struct ale { struct ale *ae_next; /* Next Entry */ char *ae_data; /* Entry buffer */ int ae_flags; /* Entry flags */ }; #define AE_VALID 0x0001 /* Entry has valid data */ /* waitok options */ #define ALQ_NOWAIT 0x0001 #define ALQ_WAITOK 0x0002 +/* Suggested mode for file creation. */ +#define ALQ_DEFAULT_CMODE 0600 + /* * alq_open: Creates a new queue * * Arguments: * alq Storage for a pointer to the newly created queue. * file The filename to open for logging. + * cred Credential to authorize open and I/O with. + * cmode Creation mode for file, if new. * size The size of each entry in the queue. * count The number of items in the buffer, this should be large enough * to store items over the period of a disk write. * Returns: * error from open or 0 on success */ struct ucred; -int alq_open(struct alq **, const char *file, struct ucred *cred, int size, - int count); +int alq_open(struct alq **, const char *file, struct ucred *cred, int cmode, + int size, int count); /* * alq_write: Write data into the queue * * Arguments: * alq The queue we're writing to * data The entry to be recorded * waitok Are we permitted to wait? * * Returns: * EWOULDBLOCK if: * Waitok is ALQ_NOWAIT and the queue is full. * The system is shutting down. * 0 on success. */ int alq_write(struct alq *alq, void *data, int waitok); /* * alq_flush: Flush the queue out to disk */ void alq_flush(struct alq *alq); /* * alq_close: Flush the queue and free all resources. */ void alq_close(struct alq *alq); /* * alq_get: Return an entry for direct access * * Arguments: * alq The queue to retrieve an entry from * waitok Are we permitted to wait? * * Returns: * The next available ale on success. * NULL if: * Waitok is ALQ_NOWAIT and the queue is full. * The system is shutting down. * * This leaves the queue locked until a subsequent alq_post. */ struct ale *alq_get(struct alq *alq, int waitok); /* * alq_post: Schedule the ale retrieved by alq_get for writing. * alq The queue to post the entry to. * ale An asynch logging entry returned by alq_get. */ void alq_post(struct alq *, struct ale *); #endif /* _SYS_ALQ_H_ */