2 * Copyright (c) 2002, Jeffrey Roberson <jeff@freebsd.org>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice unmodified, this list of conditions, and the following
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/kernel.h>
35 #include <sys/kthread.h>
37 #include <sys/mount.h>
38 #include <sys/mutex.h>
39 #include <sys/namei.h>
41 #include <sys/vnode.h>
43 #include <sys/malloc.h>
44 #include <sys/unistd.h>
45 #include <sys/fcntl.h>
46 #include <sys/eventhandler.h>
48 #include <security/mac/mac_framework.h>
50 /* Async. Logging Queue */
52 int aq_entmax
; /* Max entries */
53 int aq_entlen
; /* Entry length */
54 char *aq_entbuf
; /* Buffer for stored entries */
55 int aq_flags
; /* Queue flags */
56 struct mtx aq_mtx
; /* Queue lock */
57 struct vnode
*aq_vp
; /* Open vnode handle */
58 struct ucred
*aq_cred
; /* Credentials of the opening thread */
59 struct ale
*aq_first
; /* First ent */
60 struct ale
*aq_entfree
; /* First free ent */
61 struct ale
*aq_entvalid
; /* First ent valid for writing */
62 LIST_ENTRY(alq
) aq_act
; /* List of active queues */
63 LIST_ENTRY(alq
) aq_link
; /* List of all queues */
66 #define AQ_WANTED 0x0001 /* Wakeup sleeper when io is done */
67 #define AQ_ACTIVE 0x0002 /* on the active list */
68 #define AQ_FLUSHING 0x0004 /* doing IO */
69 #define AQ_SHUTDOWN 0x0008 /* Queue no longer valid */
71 #define ALQ_LOCK(alq) mtx_lock_spin(&(alq)->aq_mtx)
72 #define ALQ_UNLOCK(alq) mtx_unlock_spin(&(alq)->aq_mtx)
74 static MALLOC_DEFINE(M_ALD
, "ALD", "ALD");
77 * The ald_mtx protects the ald_queues list and the ald_active list.
79 static struct mtx ald_mtx
;
80 static LIST_HEAD(, alq
) ald_queues
;
81 static LIST_HEAD(, alq
) ald_active
;
82 static int ald_shutingdown
= 0;
83 struct thread
*ald_thread
;
84 static struct proc
*ald_proc
;
86 #define ALD_LOCK() mtx_lock(&ald_mtx)
87 #define ALD_UNLOCK() mtx_unlock(&ald_mtx)
89 /* Daemon functions */
90 static int ald_add(struct alq
*);
91 static int ald_rem(struct alq
*);
92 static void ald_startup(void *);
93 static void ald_daemon(void);
94 static void ald_shutdown(void *, int);
95 static void ald_activate(struct alq
*);
96 static void ald_deactivate(struct alq
*);
98 /* Internal queue functions */
99 static void alq_shutdown(struct alq
*);
100 static int alq_doio(struct alq
*);
104 * Add a new queue to the global list. Fail if we're shutting down.
107 ald_add(struct alq
*alq
)
114 if (ald_shutingdown
) {
118 LIST_INSERT_HEAD(&ald_queues
, alq
, aq_link
);
125 * Remove a queue from the global list unless we're shutting down. If so,
126 * the ald will take care of cleaning up it's resources.
129 ald_rem(struct alq
*alq
)
136 if (ald_shutingdown
) {
140 LIST_REMOVE(alq
, aq_link
);
147 * Put a queue on the active list. This will schedule it for writing.
150 ald_activate(struct alq
*alq
)
152 LIST_INSERT_HEAD(&ald_active
, alq
, aq_act
);
157 ald_deactivate(struct alq
*alq
)
159 LIST_REMOVE(alq
, aq_act
);
160 alq
->aq_flags
&= ~AQ_ACTIVE
;
164 ald_startup(void *unused
)
166 mtx_init(&ald_mtx
, "ALDmtx", NULL
, MTX_DEF
|MTX_QUIET
);
167 LIST_INIT(&ald_queues
);
168 LIST_INIT(&ald_active
);
177 ald_thread
= FIRST_THREAD_IN_PROC(ald_proc
);
179 EVENTHANDLER_REGISTER(shutdown_pre_sync
, ald_shutdown
, NULL
,
185 while ((alq
= LIST_FIRST(&ald_active
)) == NULL
)
186 msleep(&ald_active
, &ald_mtx
, PWAIT
, "aldslp", 0);
191 needwakeup
= alq_doio(alq
);
200 ald_shutdown(void *arg
, int howto
)
207 while ((alq
= LIST_FIRST(&ald_queues
)) != NULL
) {
208 LIST_REMOVE(alq
, aq_link
);
217 alq_shutdown(struct alq
*alq
)
221 /* Stop any new writers. */
222 alq
->aq_flags
|= AQ_SHUTDOWN
;
225 while (alq
->aq_flags
& (AQ_FLUSHING
|AQ_ACTIVE
)) {
226 alq
->aq_flags
|= AQ_WANTED
;
227 msleep_spin(alq
, &alq
->aq_mtx
, "aldclose", 0);
231 vn_close(alq
->aq_vp
, FWRITE
, alq
->aq_cred
,
233 crfree(alq
->aq_cred
);
237 * Flush all pending data to disk. This operation will block.
240 alq_doio(struct alq
*alq
)
246 struct iovec aiov
[2];
258 alstart
= ale
= alq
->aq_entvalid
;
259 alq
->aq_entvalid
= NULL
;
261 bzero(&aiov
, sizeof(aiov
));
262 bzero(&auio
, sizeof(auio
));
265 if (aiov
[iov
].iov_base
== NULL
)
266 aiov
[iov
].iov_base
= ale
->ae_data
;
267 aiov
[iov
].iov_len
+= alq
->aq_entlen
;
268 totlen
+= alq
->aq_entlen
;
269 /* Check to see if we're wrapping the buffer */
270 if (ale
->ae_data
+ alq
->aq_entlen
!= ale
->ae_next
->ae_data
)
272 ale
->ae_flags
&= ~AE_VALID
;
274 } while (ale
->ae_flags
& AE_VALID
);
276 alq
->aq_flags
|= AQ_FLUSHING
;
279 if (iov
== 2 || aiov
[iov
].iov_base
== NULL
)
282 auio
.uio_iov
= &aiov
[0];
284 auio
.uio_segflg
= UIO_SYSSPACE
;
285 auio
.uio_rw
= UIO_WRITE
;
286 auio
.uio_iovcnt
= iov
+ 1;
287 auio
.uio_resid
= totlen
;
291 * Do all of the junk required to write now.
293 vfslocked
= VFS_LOCK_GIANT(vp
->v_mount
);
294 vn_start_write(vp
, &mp
, V_WAIT
);
295 vn_lock(vp
, LK_EXCLUSIVE
| LK_RETRY
);
296 VOP_LEASE(vp
, td
, alq
->aq_cred
, LEASE_WRITE
);
298 * XXX: VOP_WRITE error checks are ignored.
301 if (mac_vnode_check_write(alq
->aq_cred
, NOCRED
, vp
) == 0)
303 VOP_WRITE(vp
, &auio
, IO_UNIT
| IO_APPEND
, alq
->aq_cred
);
305 vn_finished_write(mp
);
306 VFS_UNLOCK_GIANT(vfslocked
);
309 alq
->aq_flags
&= ~AQ_FLUSHING
;
311 if (alq
->aq_entfree
== NULL
)
312 alq
->aq_entfree
= alstart
;
314 if (alq
->aq_flags
& AQ_WANTED
) {
315 alq
->aq_flags
&= ~AQ_WANTED
;
322 static struct kproc_desc ald_kp
= {
328 SYSINIT(aldthread
, SI_SUB_KTHREAD_IDLE
, SI_ORDER_ANY
, kproc_start
, &ald_kp
);
329 SYSINIT(ald
, SI_SUB_LOCK
, SI_ORDER_ANY
, ald_startup
, NULL
);
332 /* User visible queue functions */
335 * Create the queue data structure, allocate the buffer, and open the file.
338 alq_open(struct alq
**alqp
, const char *file
, struct ucred
*cred
, int cmode
,
354 NDINIT(&nd
, LOOKUP
, NOFOLLOW
| MPSAFE
, UIO_SYSSPACE
, file
, td
);
355 flags
= FWRITE
| O_NOFOLLOW
| O_CREAT
;
357 error
= vn_open_cred(&nd
, &flags
, cmode
, cred
, NULL
);
361 vfslocked
= NDHASGIANT(&nd
);
362 NDFREE(&nd
, NDF_ONLY_PNBUF
);
363 /* We just unlock so we hold a reference */
364 VOP_UNLOCK(nd
.ni_vp
, 0);
365 VFS_UNLOCK_GIANT(vfslocked
);
367 alq
= malloc(sizeof(*alq
), M_ALD
, M_WAITOK
|M_ZERO
);
368 alq
->aq_entbuf
= malloc(count
* size
, M_ALD
, M_WAITOK
|M_ZERO
);
369 alq
->aq_first
= malloc(sizeof(*ale
) * count
, M_ALD
, M_WAITOK
|M_ZERO
);
370 alq
->aq_vp
= nd
.ni_vp
;
371 alq
->aq_cred
= crhold(cred
);
372 alq
->aq_entmax
= count
;
373 alq
->aq_entlen
= size
;
374 alq
->aq_entfree
= alq
->aq_first
;
376 mtx_init(&alq
->aq_mtx
, "ALD Queue", NULL
, MTX_SPIN
|MTX_QUIET
);
378 bufp
= alq
->aq_entbuf
;
382 /* Match up entries with buffers */
383 for (i
= 0; i
< count
; i
++) {
392 alp
->ae_next
= alq
->aq_first
;
394 if ((error
= ald_add(alq
)) != 0)
402 * Copy a new entry into the queue. If the operation would block either
403 * wait or return an error depending on the value of waitok.
406 alq_write(struct alq
*alq
, void *data
, int waitok
)
410 if ((ale
= alq_get(alq
, waitok
)) == NULL
)
411 return (EWOULDBLOCK
);
413 bcopy(data
, ale
->ae_data
, alq
->aq_entlen
);
420 alq_get(struct alq
*alq
, int waitok
)
429 /* Loop until we get an entry or we're shutting down */
430 while ((alq
->aq_flags
& AQ_SHUTDOWN
) == 0 &&
431 (ale
= alq
->aq_entfree
) == NULL
&&
432 (waitok
& ALQ_WAITOK
)) {
433 alq
->aq_flags
|= AQ_WANTED
;
434 msleep_spin(alq
, &alq
->aq_mtx
, "alqget", 0);
439 if ((aln
->ae_flags
& AE_VALID
) == 0)
440 alq
->aq_entfree
= aln
;
442 alq
->aq_entfree
= NULL
;
451 alq_post(struct alq
*alq
, struct ale
*ale
)
455 ale
->ae_flags
|= AE_VALID
;
457 if (alq
->aq_entvalid
== NULL
)
458 alq
->aq_entvalid
= ale
;
460 if ((alq
->aq_flags
& AQ_ACTIVE
) == 0) {
461 alq
->aq_flags
|= AQ_ACTIVE
;
475 alq_flush(struct alq
*alq
)
481 if (alq
->aq_flags
& AQ_ACTIVE
) {
484 needwakeup
= alq_doio(alq
);
494 * Flush remaining data, close the file and free all resources.
497 alq_close(struct alq
*alq
)
500 * If we're already shuting down someone else will flush and close
503 if (ald_rem(alq
) != 0)
507 * Drain all pending IO.
511 mtx_destroy(&alq
->aq_mtx
);
512 free(alq
->aq_first
, M_ALD
);
513 free(alq
->aq_entbuf
, M_ALD
);