xref: /freebsd/sys/kern/kern_alq.c (revision 97c11ef2828c7c1024f22a294dbaed42db6f23dd)
19454b2d8SWarner Losh /*-
29405072aSJeff Roberson  * Copyright (c) 2002, Jeffrey Roberson <jeff@freebsd.org>
3d28f42f9SLawrence Stewart  * Copyright (c) 2008-2009, Lawrence Stewart <lstewart@freebsd.org>
4d28f42f9SLawrence Stewart  * Copyright (c) 2009-2010, The FreeBSD Foundation
59405072aSJeff Roberson  * All rights reserved.
69405072aSJeff Roberson  *
7d28f42f9SLawrence Stewart  * Portions of this software were developed at the Centre for Advanced
8d28f42f9SLawrence Stewart  * Internet Architectures, Swinburne University of Technology, Melbourne,
9d28f42f9SLawrence Stewart  * Australia by Lawrence Stewart under sponsorship from the FreeBSD Foundation.
10d28f42f9SLawrence Stewart  *
119405072aSJeff Roberson  * Redistribution and use in source and binary forms, with or without
129405072aSJeff Roberson  * modification, are permitted provided that the following conditions
139405072aSJeff Roberson  * are met:
149405072aSJeff Roberson  * 1. Redistributions of source code must retain the above copyright
159405072aSJeff Roberson  *    notice unmodified, this list of conditions, and the following
169405072aSJeff Roberson  *    disclaimer.
179405072aSJeff Roberson  * 2. Redistributions in binary form must reproduce the above copyright
189405072aSJeff Roberson  *    notice, this list of conditions and the following disclaimer in the
199405072aSJeff Roberson  *    documentation and/or other materials provided with the distribution.
209405072aSJeff Roberson  *
219405072aSJeff Roberson  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
229405072aSJeff Roberson  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
239405072aSJeff Roberson  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
249405072aSJeff Roberson  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
259405072aSJeff Roberson  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
269405072aSJeff Roberson  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
279405072aSJeff Roberson  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
289405072aSJeff Roberson  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
299405072aSJeff Roberson  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
309405072aSJeff Roberson  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
319405072aSJeff Roberson  */
329405072aSJeff Roberson 
33677b542eSDavid E. O'Brien #include <sys/cdefs.h>
34677b542eSDavid E. O'Brien __FBSDID("$FreeBSD$");
35677b542eSDavid E. O'Brien 
36d28f42f9SLawrence Stewart #include "opt_mac.h"
37d28f42f9SLawrence Stewart 
389405072aSJeff Roberson #include <sys/param.h>
399405072aSJeff Roberson #include <sys/systm.h>
409405072aSJeff Roberson #include <sys/kernel.h>
419405072aSJeff Roberson #include <sys/kthread.h>
429405072aSJeff Roberson #include <sys/lock.h>
4333f19beeSJohn Baldwin #include <sys/mount.h>
449405072aSJeff Roberson #include <sys/mutex.h>
459405072aSJeff Roberson #include <sys/namei.h>
469405072aSJeff Roberson #include <sys/proc.h>
479405072aSJeff Roberson #include <sys/vnode.h>
489405072aSJeff Roberson #include <sys/alq.h>
499405072aSJeff Roberson #include <sys/malloc.h>
509405072aSJeff Roberson #include <sys/unistd.h>
519405072aSJeff Roberson #include <sys/fcntl.h>
529405072aSJeff Roberson #include <sys/eventhandler.h>
539405072aSJeff Roberson 
54aed55708SRobert Watson #include <security/mac/mac_framework.h>
55aed55708SRobert Watson 
569405072aSJeff Roberson /* Async. Logging Queue */
579405072aSJeff Roberson struct alq {
589405072aSJeff Roberson 	int	aq_entmax;		/* Max entries */
599405072aSJeff Roberson 	int	aq_entlen;		/* Entry length */
609405072aSJeff Roberson 	char	*aq_entbuf;		/* Buffer for stored entries */
619405072aSJeff Roberson 	int	aq_flags;		/* Queue flags */
629405072aSJeff Roberson 	struct mtx	aq_mtx;		/* Queue lock */
639405072aSJeff Roberson 	struct vnode	*aq_vp;		/* Open vnode handle */
649e9256e2SJeff Roberson 	struct ucred	*aq_cred;	/* Credentials of the opening thread */
659405072aSJeff Roberson 	struct ale	*aq_first;	/* First ent */
669405072aSJeff Roberson 	struct ale	*aq_entfree;	/* First free ent */
679405072aSJeff Roberson 	struct ale	*aq_entvalid;	/* First ent valid for writing */
689405072aSJeff Roberson 	LIST_ENTRY(alq)	aq_act;		/* List of active queues */
699405072aSJeff Roberson 	LIST_ENTRY(alq)	aq_link;	/* List of all queues */
709405072aSJeff Roberson };
719405072aSJeff Roberson 
729405072aSJeff Roberson #define	AQ_WANTED	0x0001		/* Wakeup sleeper when io is done */
739405072aSJeff Roberson #define	AQ_ACTIVE	0x0002		/* on the active list */
749405072aSJeff Roberson #define	AQ_FLUSHING	0x0004		/* doing IO */
759405072aSJeff Roberson #define	AQ_SHUTDOWN	0x0008		/* Queue no longer valid */
769405072aSJeff Roberson 
779405072aSJeff Roberson #define	ALQ_LOCK(alq)	mtx_lock_spin(&(alq)->aq_mtx)
789405072aSJeff Roberson #define	ALQ_UNLOCK(alq)	mtx_unlock_spin(&(alq)->aq_mtx)
799405072aSJeff Roberson 
809405072aSJeff Roberson static MALLOC_DEFINE(M_ALD, "ALD", "ALD");
819405072aSJeff Roberson 
829405072aSJeff Roberson /*
839405072aSJeff Roberson  * The ald_mtx protects the ald_queues list and the ald_active list.
849405072aSJeff Roberson  */
859405072aSJeff Roberson static struct mtx ald_mtx;
869405072aSJeff Roberson static LIST_HEAD(, alq) ald_queues;
879405072aSJeff Roberson static LIST_HEAD(, alq) ald_active;
889405072aSJeff Roberson static int ald_shutingdown = 0;
89a414302fSJeff Roberson struct thread *ald_thread;
90a414302fSJeff Roberson static struct proc *ald_proc;
919405072aSJeff Roberson 
929405072aSJeff Roberson #define	ALD_LOCK()	mtx_lock(&ald_mtx)
939405072aSJeff Roberson #define	ALD_UNLOCK()	mtx_unlock(&ald_mtx)
949405072aSJeff Roberson 
959405072aSJeff Roberson /* Daemon functions */
969405072aSJeff Roberson static int ald_add(struct alq *);
979405072aSJeff Roberson static int ald_rem(struct alq *);
989405072aSJeff Roberson static void ald_startup(void *);
999405072aSJeff Roberson static void ald_daemon(void);
1009405072aSJeff Roberson static void ald_shutdown(void *, int);
1019405072aSJeff Roberson static void ald_activate(struct alq *);
1029405072aSJeff Roberson static void ald_deactivate(struct alq *);
1039405072aSJeff Roberson 
1049405072aSJeff Roberson /* Internal queue functions */
1059405072aSJeff Roberson static void alq_shutdown(struct alq *);
106c0ea37a8SLawrence Stewart static void alq_destroy(struct alq *);
1079405072aSJeff Roberson static int alq_doio(struct alq *);
1089405072aSJeff Roberson 
1099405072aSJeff Roberson 
1109405072aSJeff Roberson /*
1119405072aSJeff Roberson  * Add a new queue to the global list.  Fail if we're shutting down.
1129405072aSJeff Roberson  */
1139405072aSJeff Roberson static int
1149405072aSJeff Roberson ald_add(struct alq *alq)
1159405072aSJeff Roberson {
1169405072aSJeff Roberson 	int error;
1179405072aSJeff Roberson 
1189405072aSJeff Roberson 	error = 0;
1199405072aSJeff Roberson 
1209405072aSJeff Roberson 	ALD_LOCK();
1219405072aSJeff Roberson 	if (ald_shutingdown) {
1229405072aSJeff Roberson 		error = EBUSY;
1239405072aSJeff Roberson 		goto done;
1249405072aSJeff Roberson 	}
1259405072aSJeff Roberson 	LIST_INSERT_HEAD(&ald_queues, alq, aq_link);
1269405072aSJeff Roberson done:
1279405072aSJeff Roberson 	ALD_UNLOCK();
1289405072aSJeff Roberson 	return (error);
1299405072aSJeff Roberson }
1309405072aSJeff Roberson 
1319405072aSJeff Roberson /*
1329405072aSJeff Roberson  * Remove a queue from the global list unless we're shutting down.  If so,
1339405072aSJeff Roberson  * the ald will take care of cleaning up it's resources.
1349405072aSJeff Roberson  */
1359405072aSJeff Roberson static int
1369405072aSJeff Roberson ald_rem(struct alq *alq)
1379405072aSJeff Roberson {
1389405072aSJeff Roberson 	int error;
1399405072aSJeff Roberson 
1409405072aSJeff Roberson 	error = 0;
1419405072aSJeff Roberson 
1429405072aSJeff Roberson 	ALD_LOCK();
1439405072aSJeff Roberson 	if (ald_shutingdown) {
1449405072aSJeff Roberson 		error = EBUSY;
1459405072aSJeff Roberson 		goto done;
1469405072aSJeff Roberson 	}
1479405072aSJeff Roberson 	LIST_REMOVE(alq, aq_link);
1489405072aSJeff Roberson done:
1499405072aSJeff Roberson 	ALD_UNLOCK();
1509405072aSJeff Roberson 	return (error);
1519405072aSJeff Roberson }
1529405072aSJeff Roberson 
1539405072aSJeff Roberson /*
1549405072aSJeff Roberson  * Put a queue on the active list.  This will schedule it for writing.
1559405072aSJeff Roberson  */
1569405072aSJeff Roberson static void
1579405072aSJeff Roberson ald_activate(struct alq *alq)
1589405072aSJeff Roberson {
1599405072aSJeff Roberson 	LIST_INSERT_HEAD(&ald_active, alq, aq_act);
1609405072aSJeff Roberson 	wakeup(&ald_active);
1619405072aSJeff Roberson }
1629405072aSJeff Roberson 
1639405072aSJeff Roberson static void
1649405072aSJeff Roberson ald_deactivate(struct alq *alq)
1659405072aSJeff Roberson {
1669405072aSJeff Roberson 	LIST_REMOVE(alq, aq_act);
1679405072aSJeff Roberson 	alq->aq_flags &= ~AQ_ACTIVE;
1689405072aSJeff Roberson }
1699405072aSJeff Roberson 
1709405072aSJeff Roberson static void
1719405072aSJeff Roberson ald_startup(void *unused)
1729405072aSJeff Roberson {
1739405072aSJeff Roberson 	mtx_init(&ald_mtx, "ALDmtx", NULL, MTX_DEF|MTX_QUIET);
1749405072aSJeff Roberson 	LIST_INIT(&ald_queues);
1759405072aSJeff Roberson 	LIST_INIT(&ald_active);
1769405072aSJeff Roberson }
1779405072aSJeff Roberson 
1789405072aSJeff Roberson static void
1799405072aSJeff Roberson ald_daemon(void)
1809405072aSJeff Roberson {
1819405072aSJeff Roberson 	int needwakeup;
1829405072aSJeff Roberson 	struct alq *alq;
1839405072aSJeff Roberson 
184a414302fSJeff Roberson 	ald_thread = FIRST_THREAD_IN_PROC(ald_proc);
185a414302fSJeff Roberson 
1869405072aSJeff Roberson 	EVENTHANDLER_REGISTER(shutdown_pre_sync, ald_shutdown, NULL,
1879405072aSJeff Roberson 	    SHUTDOWN_PRI_FIRST);
1889405072aSJeff Roberson 
1899405072aSJeff Roberson 	ALD_LOCK();
1909405072aSJeff Roberson 
1919405072aSJeff Roberson 	for (;;) {
192d28f42f9SLawrence Stewart 		while ((alq = LIST_FIRST(&ald_active)) == NULL &&
193d28f42f9SLawrence Stewart 		    !ald_shutingdown)
1949ffad7a9SLawrence Stewart 			mtx_sleep(&ald_active, &ald_mtx, PWAIT, "aldslp", 0);
1959405072aSJeff Roberson 
196d28f42f9SLawrence Stewart 		/* Don't shutdown until all active ALQs are flushed. */
197d28f42f9SLawrence Stewart 		if (ald_shutingdown && alq == NULL) {
198d28f42f9SLawrence Stewart 			ALD_UNLOCK();
199d28f42f9SLawrence Stewart 			break;
200d28f42f9SLawrence Stewart 		}
201d28f42f9SLawrence Stewart 
2029405072aSJeff Roberson 		ALQ_LOCK(alq);
2039405072aSJeff Roberson 		ald_deactivate(alq);
2049405072aSJeff Roberson 		ALD_UNLOCK();
2059405072aSJeff Roberson 		needwakeup = alq_doio(alq);
2069405072aSJeff Roberson 		ALQ_UNLOCK(alq);
2079405072aSJeff Roberson 		if (needwakeup)
2089405072aSJeff Roberson 			wakeup(alq);
2099405072aSJeff Roberson 		ALD_LOCK();
2109405072aSJeff Roberson 	}
211d28f42f9SLawrence Stewart 
212d28f42f9SLawrence Stewart 	kproc_exit(0);
2139405072aSJeff Roberson }
2149405072aSJeff Roberson 
2159405072aSJeff Roberson static void
2169405072aSJeff Roberson ald_shutdown(void *arg, int howto)
2179405072aSJeff Roberson {
2189405072aSJeff Roberson 	struct alq *alq;
2199405072aSJeff Roberson 
2209405072aSJeff Roberson 	ALD_LOCK();
221d28f42f9SLawrence Stewart 
222d28f42f9SLawrence Stewart 	/* Ensure no new queues can be created. */
2239405072aSJeff Roberson 	ald_shutingdown = 1;
2249405072aSJeff Roberson 
225d28f42f9SLawrence Stewart 	/* Shutdown all ALQs prior to terminating the ald_daemon. */
2269405072aSJeff Roberson 	while ((alq = LIST_FIRST(&ald_queues)) != NULL) {
2279405072aSJeff Roberson 		LIST_REMOVE(alq, aq_link);
2289405072aSJeff Roberson 		ALD_UNLOCK();
2299405072aSJeff Roberson 		alq_shutdown(alq);
2309405072aSJeff Roberson 		ALD_LOCK();
2319405072aSJeff Roberson 	}
232d28f42f9SLawrence Stewart 
233d28f42f9SLawrence Stewart 	/* At this point, all ALQs are flushed and shutdown. */
234d28f42f9SLawrence Stewart 
235d28f42f9SLawrence Stewart 	/*
236d28f42f9SLawrence Stewart 	 * Wake ald_daemon so that it exits. It won't be able to do
2379ffad7a9SLawrence Stewart 	 * anything until we mtx_sleep because we hold the ald_mtx.
238d28f42f9SLawrence Stewart 	 */
239d28f42f9SLawrence Stewart 	wakeup(&ald_active);
240d28f42f9SLawrence Stewart 
241d28f42f9SLawrence Stewart 	/* Wait for ald_daemon to exit. */
2429ffad7a9SLawrence Stewart 	mtx_sleep(ald_proc, &ald_mtx, PWAIT, "aldslp", 0);
243d28f42f9SLawrence Stewart 
2449405072aSJeff Roberson 	ALD_UNLOCK();
2459405072aSJeff Roberson }
2469405072aSJeff Roberson 
2479405072aSJeff Roberson static void
2489405072aSJeff Roberson alq_shutdown(struct alq *alq)
2499405072aSJeff Roberson {
2509405072aSJeff Roberson 	ALQ_LOCK(alq);
2519405072aSJeff Roberson 
2529405072aSJeff Roberson 	/* Stop any new writers. */
2539405072aSJeff Roberson 	alq->aq_flags |= AQ_SHUTDOWN;
2549405072aSJeff Roberson 
2559405072aSJeff Roberson 	/* Drain IO */
25697c11ef2SLawrence Stewart 	while (alq->aq_flags & AQ_ACTIVE) {
2579405072aSJeff Roberson 		alq->aq_flags |= AQ_WANTED;
258bff2d4d5SRoman Divacky 		msleep_spin(alq, &alq->aq_mtx, "aldclose", 0);
2599405072aSJeff Roberson 	}
2609405072aSJeff Roberson 	ALQ_UNLOCK(alq);
2619405072aSJeff Roberson 
262a414302fSJeff Roberson 	vn_close(alq->aq_vp, FWRITE, alq->aq_cred,
2639e9256e2SJeff Roberson 	    curthread);
2649e9256e2SJeff Roberson 	crfree(alq->aq_cred);
2659405072aSJeff Roberson }
2669405072aSJeff Roberson 
267c0ea37a8SLawrence Stewart void
268c0ea37a8SLawrence Stewart alq_destroy(struct alq *alq)
269c0ea37a8SLawrence Stewart {
270c0ea37a8SLawrence Stewart 	/* Drain all pending IO. */
271c0ea37a8SLawrence Stewart 	alq_shutdown(alq);
272c0ea37a8SLawrence Stewart 
273c0ea37a8SLawrence Stewart 	mtx_destroy(&alq->aq_mtx);
274c0ea37a8SLawrence Stewart 	free(alq->aq_first, M_ALD);
275c0ea37a8SLawrence Stewart 	free(alq->aq_entbuf, M_ALD);
276c0ea37a8SLawrence Stewart 	free(alq, M_ALD);
277c0ea37a8SLawrence Stewart }
278c0ea37a8SLawrence Stewart 
2799405072aSJeff Roberson /*
2809405072aSJeff Roberson  * Flush all pending data to disk.  This operation will block.
2819405072aSJeff Roberson  */
2829405072aSJeff Roberson static int
2839405072aSJeff Roberson alq_doio(struct alq *alq)
2849405072aSJeff Roberson {
2859405072aSJeff Roberson 	struct thread *td;
2869405072aSJeff Roberson 	struct mount *mp;
2879405072aSJeff Roberson 	struct vnode *vp;
2889405072aSJeff Roberson 	struct uio auio;
2899405072aSJeff Roberson 	struct iovec aiov[2];
2909405072aSJeff Roberson 	struct ale *ale;
2919405072aSJeff Roberson 	struct ale *alstart;
2929405072aSJeff Roberson 	int totlen;
2939405072aSJeff Roberson 	int iov;
29433f19beeSJohn Baldwin 	int vfslocked;
2959405072aSJeff Roberson 
2969405072aSJeff Roberson 	vp = alq->aq_vp;
2979405072aSJeff Roberson 	td = curthread;
2989405072aSJeff Roberson 	totlen = 0;
2999405072aSJeff Roberson 	iov = 0;
3009405072aSJeff Roberson 
3019405072aSJeff Roberson 	alstart = ale = alq->aq_entvalid;
3029405072aSJeff Roberson 	alq->aq_entvalid = NULL;
3039405072aSJeff Roberson 
3049405072aSJeff Roberson 	bzero(&aiov, sizeof(aiov));
3059405072aSJeff Roberson 	bzero(&auio, sizeof(auio));
3069405072aSJeff Roberson 
3079405072aSJeff Roberson 	do {
3089405072aSJeff Roberson 		if (aiov[iov].iov_base == NULL)
3099405072aSJeff Roberson 			aiov[iov].iov_base = ale->ae_data;
3109405072aSJeff Roberson 		aiov[iov].iov_len += alq->aq_entlen;
3119405072aSJeff Roberson 		totlen += alq->aq_entlen;
3129405072aSJeff Roberson 		/* Check to see if we're wrapping the buffer */
3139405072aSJeff Roberson 		if (ale->ae_data + alq->aq_entlen != ale->ae_next->ae_data)
3149405072aSJeff Roberson 			iov++;
3159405072aSJeff Roberson 		ale->ae_flags &= ~AE_VALID;
3169405072aSJeff Roberson 		ale = ale->ae_next;
3179405072aSJeff Roberson 	} while (ale->ae_flags & AE_VALID);
3189405072aSJeff Roberson 
3199405072aSJeff Roberson 	alq->aq_flags |= AQ_FLUSHING;
3209405072aSJeff Roberson 	ALQ_UNLOCK(alq);
3219405072aSJeff Roberson 
3229405072aSJeff Roberson 	if (iov == 2 || aiov[iov].iov_base == NULL)
3239405072aSJeff Roberson 		iov--;
3249405072aSJeff Roberson 
3259405072aSJeff Roberson 	auio.uio_iov = &aiov[0];
3269405072aSJeff Roberson 	auio.uio_offset = 0;
3279405072aSJeff Roberson 	auio.uio_segflg = UIO_SYSSPACE;
3289405072aSJeff Roberson 	auio.uio_rw = UIO_WRITE;
3299405072aSJeff Roberson 	auio.uio_iovcnt = iov + 1;
3309405072aSJeff Roberson 	auio.uio_resid = totlen;
3319405072aSJeff Roberson 	auio.uio_td = td;
3329405072aSJeff Roberson 
3339405072aSJeff Roberson 	/*
3349405072aSJeff Roberson 	 * Do all of the junk required to write now.
3359405072aSJeff Roberson 	 */
33633f19beeSJohn Baldwin 	vfslocked = VFS_LOCK_GIANT(vp->v_mount);
3379405072aSJeff Roberson 	vn_start_write(vp, &mp, V_WAIT);
338cb05b60aSAttilio Rao 	vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
33967536f03SRobert Watson 	/*
34067536f03SRobert Watson 	 * XXX: VOP_WRITE error checks are ignored.
34167536f03SRobert Watson 	 */
34267536f03SRobert Watson #ifdef MAC
34330d239bcSRobert Watson 	if (mac_vnode_check_write(alq->aq_cred, NOCRED, vp) == 0)
34467536f03SRobert Watson #endif
3459e9256e2SJeff Roberson 		VOP_WRITE(vp, &auio, IO_UNIT | IO_APPEND, alq->aq_cred);
34622db15c0SAttilio Rao 	VOP_UNLOCK(vp, 0);
3479405072aSJeff Roberson 	vn_finished_write(mp);
34833f19beeSJohn Baldwin 	VFS_UNLOCK_GIANT(vfslocked);
3499405072aSJeff Roberson 
3509405072aSJeff Roberson 	ALQ_LOCK(alq);
3519405072aSJeff Roberson 	alq->aq_flags &= ~AQ_FLUSHING;
3529405072aSJeff Roberson 
3539405072aSJeff Roberson 	if (alq->aq_entfree == NULL)
3549405072aSJeff Roberson 		alq->aq_entfree = alstart;
3559405072aSJeff Roberson 
3569405072aSJeff Roberson 	if (alq->aq_flags & AQ_WANTED) {
3579405072aSJeff Roberson 		alq->aq_flags &= ~AQ_WANTED;
3589405072aSJeff Roberson 		return (1);
3599405072aSJeff Roberson 	}
3609405072aSJeff Roberson 
3619405072aSJeff Roberson 	return(0);
3629405072aSJeff Roberson }
3639405072aSJeff Roberson 
3649405072aSJeff Roberson static struct kproc_desc ald_kp = {
3659405072aSJeff Roberson         "ALQ Daemon",
3669405072aSJeff Roberson         ald_daemon,
367a414302fSJeff Roberson         &ald_proc
3689405072aSJeff Roberson };
3699405072aSJeff Roberson 
370237fdd78SRobert Watson SYSINIT(aldthread, SI_SUB_KTHREAD_IDLE, SI_ORDER_ANY, kproc_start, &ald_kp);
371237fdd78SRobert Watson SYSINIT(ald, SI_SUB_LOCK, SI_ORDER_ANY, ald_startup, NULL);
3729405072aSJeff Roberson 
3739405072aSJeff Roberson 
3749405072aSJeff Roberson /* User visible queue functions */
3759405072aSJeff Roberson 
3769405072aSJeff Roberson /*
3779405072aSJeff Roberson  * Create the queue data structure, allocate the buffer, and open the file.
3789405072aSJeff Roberson  */
3799405072aSJeff Roberson int
380e551d452SRobert Watson alq_open(struct alq **alqp, const char *file, struct ucred *cred, int cmode,
381e551d452SRobert Watson     int size, int count)
3829405072aSJeff Roberson {
3839405072aSJeff Roberson 	struct thread *td;
3849405072aSJeff Roberson 	struct nameidata nd;
3859405072aSJeff Roberson 	struct ale *ale;
3869405072aSJeff Roberson 	struct ale *alp;
3879405072aSJeff Roberson 	struct alq *alq;
3889405072aSJeff Roberson 	char *bufp;
3899405072aSJeff Roberson 	int flags;
3909405072aSJeff Roberson 	int error;
39133f19beeSJohn Baldwin 	int i, vfslocked;
3929405072aSJeff Roberson 
3939405072aSJeff Roberson 	*alqp = NULL;
3949405072aSJeff Roberson 	td = curthread;
3959405072aSJeff Roberson 
39633f19beeSJohn Baldwin 	NDINIT(&nd, LOOKUP, NOFOLLOW | MPSAFE, UIO_SYSSPACE, file, td);
397a414302fSJeff Roberson 	flags = FWRITE | O_NOFOLLOW | O_CREAT;
3989405072aSJeff Roberson 
399e0c161b8SKonstantin Belousov 	error = vn_open_cred(&nd, &flags, cmode, 0, cred, NULL);
4009405072aSJeff Roberson 	if (error)
4019405072aSJeff Roberson 		return (error);
4029405072aSJeff Roberson 
40333f19beeSJohn Baldwin 	vfslocked = NDHASGIANT(&nd);
404f220f7afSPawel Jakub Dawidek 	NDFREE(&nd, NDF_ONLY_PNBUF);
4059405072aSJeff Roberson 	/* We just unlock so we hold a reference */
40622db15c0SAttilio Rao 	VOP_UNLOCK(nd.ni_vp, 0);
40733f19beeSJohn Baldwin 	VFS_UNLOCK_GIANT(vfslocked);
4089405072aSJeff Roberson 
409a163d034SWarner Losh 	alq = malloc(sizeof(*alq), M_ALD, M_WAITOK|M_ZERO);
410a163d034SWarner Losh 	alq->aq_entbuf = malloc(count * size, M_ALD, M_WAITOK|M_ZERO);
411a163d034SWarner Losh 	alq->aq_first = malloc(sizeof(*ale) * count, M_ALD, M_WAITOK|M_ZERO);
4129405072aSJeff Roberson 	alq->aq_vp = nd.ni_vp;
4134b090e41SRobert Watson 	alq->aq_cred = crhold(cred);
4149405072aSJeff Roberson 	alq->aq_entmax = count;
4159405072aSJeff Roberson 	alq->aq_entlen = size;
4169405072aSJeff Roberson 	alq->aq_entfree = alq->aq_first;
4179405072aSJeff Roberson 
4189405072aSJeff Roberson 	mtx_init(&alq->aq_mtx, "ALD Queue", NULL, MTX_SPIN|MTX_QUIET);
4199405072aSJeff Roberson 
4209405072aSJeff Roberson 	bufp = alq->aq_entbuf;
4219405072aSJeff Roberson 	ale = alq->aq_first;
4229405072aSJeff Roberson 	alp = NULL;
4239405072aSJeff Roberson 
4249405072aSJeff Roberson 	/* Match up entries with buffers */
4259405072aSJeff Roberson 	for (i = 0; i < count; i++) {
4269405072aSJeff Roberson 		if (alp)
4279405072aSJeff Roberson 			alp->ae_next = ale;
4289405072aSJeff Roberson 		ale->ae_data = bufp;
4299405072aSJeff Roberson 		alp = ale;
4309405072aSJeff Roberson 		ale++;
4319405072aSJeff Roberson 		bufp += size;
4329405072aSJeff Roberson 	}
4339405072aSJeff Roberson 
4349405072aSJeff Roberson 	alp->ae_next = alq->aq_first;
4359405072aSJeff Roberson 
436c0ea37a8SLawrence Stewart 	if ((error = ald_add(alq)) != 0) {
437c0ea37a8SLawrence Stewart 		alq_destroy(alq);
4389405072aSJeff Roberson 		return (error);
439c0ea37a8SLawrence Stewart 	}
440c0ea37a8SLawrence Stewart 
4419405072aSJeff Roberson 	*alqp = alq;
4429405072aSJeff Roberson 
4439405072aSJeff Roberson 	return (0);
4449405072aSJeff Roberson }
4459405072aSJeff Roberson 
4469405072aSJeff Roberson /*
4479405072aSJeff Roberson  * Copy a new entry into the queue.  If the operation would block either
4489405072aSJeff Roberson  * wait or return an error depending on the value of waitok.
4499405072aSJeff Roberson  */
4509405072aSJeff Roberson int
4519405072aSJeff Roberson alq_write(struct alq *alq, void *data, int waitok)
4529405072aSJeff Roberson {
4539405072aSJeff Roberson 	struct ale *ale;
4549405072aSJeff Roberson 
4559405072aSJeff Roberson 	if ((ale = alq_get(alq, waitok)) == NULL)
4569405072aSJeff Roberson 		return (EWOULDBLOCK);
4579405072aSJeff Roberson 
4589405072aSJeff Roberson 	bcopy(data, ale->ae_data, alq->aq_entlen);
4599405072aSJeff Roberson 	alq_post(alq, ale);
4609405072aSJeff Roberson 
4619405072aSJeff Roberson 	return (0);
4629405072aSJeff Roberson }
4639405072aSJeff Roberson 
4649405072aSJeff Roberson struct ale *
4659405072aSJeff Roberson alq_get(struct alq *alq, int waitok)
4669405072aSJeff Roberson {
4679405072aSJeff Roberson 	struct ale *ale;
4689405072aSJeff Roberson 	struct ale *aln;
4699405072aSJeff Roberson 
4709405072aSJeff Roberson 	ale = NULL;
4719405072aSJeff Roberson 
4729405072aSJeff Roberson 	ALQ_LOCK(alq);
4739405072aSJeff Roberson 
4749405072aSJeff Roberson 	/* Loop until we get an entry or we're shutting down */
4759405072aSJeff Roberson 	while ((alq->aq_flags & AQ_SHUTDOWN) == 0 &&
4769405072aSJeff Roberson 	    (ale = alq->aq_entfree) == NULL &&
4779405072aSJeff Roberson 	    (waitok & ALQ_WAITOK)) {
4789405072aSJeff Roberson 		alq->aq_flags |= AQ_WANTED;
479bff2d4d5SRoman Divacky 		msleep_spin(alq, &alq->aq_mtx, "alqget", 0);
4809405072aSJeff Roberson 	}
4819405072aSJeff Roberson 
4829405072aSJeff Roberson 	if (ale != NULL) {
4839405072aSJeff Roberson 		aln = ale->ae_next;
4849405072aSJeff Roberson 		if ((aln->ae_flags & AE_VALID) == 0)
4859405072aSJeff Roberson 			alq->aq_entfree = aln;
48630fd5d08SJeff Roberson 		else
48730fd5d08SJeff Roberson 			alq->aq_entfree = NULL;
4889405072aSJeff Roberson 	} else
4899405072aSJeff Roberson 		ALQ_UNLOCK(alq);
4909405072aSJeff Roberson 
4919405072aSJeff Roberson 
4929405072aSJeff Roberson 	return (ale);
4939405072aSJeff Roberson }
4949405072aSJeff Roberson 
4959405072aSJeff Roberson void
4969405072aSJeff Roberson alq_post(struct alq *alq, struct ale *ale)
4979405072aSJeff Roberson {
4989405072aSJeff Roberson 	int activate;
4999405072aSJeff Roberson 
5009405072aSJeff Roberson 	ale->ae_flags |= AE_VALID;
5019405072aSJeff Roberson 
5029405072aSJeff Roberson 	if (alq->aq_entvalid == NULL)
5039405072aSJeff Roberson 		alq->aq_entvalid = ale;
5049405072aSJeff Roberson 
5059405072aSJeff Roberson 	if ((alq->aq_flags & AQ_ACTIVE) == 0) {
5069405072aSJeff Roberson 		alq->aq_flags |= AQ_ACTIVE;
5079405072aSJeff Roberson 		activate = 1;
5089405072aSJeff Roberson 	} else
5099405072aSJeff Roberson 		activate = 0;
5109405072aSJeff Roberson 
5119405072aSJeff Roberson 	ALQ_UNLOCK(alq);
5129405072aSJeff Roberson 	if (activate) {
5139405072aSJeff Roberson 		ALD_LOCK();
5149405072aSJeff Roberson 		ald_activate(alq);
5159405072aSJeff Roberson 		ALD_UNLOCK();
5169405072aSJeff Roberson 	}
5179405072aSJeff Roberson }
5189405072aSJeff Roberson 
5199405072aSJeff Roberson void
5209405072aSJeff Roberson alq_flush(struct alq *alq)
5219405072aSJeff Roberson {
5229405072aSJeff Roberson 	int needwakeup = 0;
5239405072aSJeff Roberson 
5249405072aSJeff Roberson 	ALD_LOCK();
5259405072aSJeff Roberson 	ALQ_LOCK(alq);
5269405072aSJeff Roberson 	if (alq->aq_flags & AQ_ACTIVE) {
5279405072aSJeff Roberson 		ald_deactivate(alq);
5289405072aSJeff Roberson 		ALD_UNLOCK();
5299405072aSJeff Roberson 		needwakeup = alq_doio(alq);
5309405072aSJeff Roberson 	} else
5319405072aSJeff Roberson 		ALD_UNLOCK();
5329405072aSJeff Roberson 	ALQ_UNLOCK(alq);
5339405072aSJeff Roberson 
5349405072aSJeff Roberson 	if (needwakeup)
5359405072aSJeff Roberson 		wakeup(alq);
5369405072aSJeff Roberson }
5379405072aSJeff Roberson 
5389405072aSJeff Roberson /*
5399405072aSJeff Roberson  * Flush remaining data, close the file and free all resources.
5409405072aSJeff Roberson  */
5419405072aSJeff Roberson void
5429405072aSJeff Roberson alq_close(struct alq *alq)
5439405072aSJeff Roberson {
544c0ea37a8SLawrence Stewart 	/* Only flush and destroy alq if not already shutting down. */
545c0ea37a8SLawrence Stewart 	if (ald_rem(alq) == 0)
546c0ea37a8SLawrence Stewart 		alq_destroy(alq);
5479405072aSJeff Roberson }
548d28f42f9SLawrence Stewart 
549d28f42f9SLawrence Stewart static int
550d28f42f9SLawrence Stewart alq_load_handler(module_t mod, int what, void *arg)
551d28f42f9SLawrence Stewart {
552d28f42f9SLawrence Stewart 	int ret;
553d28f42f9SLawrence Stewart 
554d28f42f9SLawrence Stewart 	ret = 0;
555d28f42f9SLawrence Stewart 
556d28f42f9SLawrence Stewart 	switch (what) {
557d28f42f9SLawrence Stewart 	case MOD_LOAD:
558d28f42f9SLawrence Stewart 	case MOD_SHUTDOWN:
559d28f42f9SLawrence Stewart 		break;
560d28f42f9SLawrence Stewart 
561d28f42f9SLawrence Stewart 	case MOD_QUIESCE:
562d28f42f9SLawrence Stewart 		ALD_LOCK();
563d28f42f9SLawrence Stewart 		/* Only allow unload if there are no open queues. */
564d28f42f9SLawrence Stewart 		if (LIST_FIRST(&ald_queues) == NULL) {
565d28f42f9SLawrence Stewart 			ald_shutingdown = 1;
566d28f42f9SLawrence Stewart 			ALD_UNLOCK();
567d28f42f9SLawrence Stewart 			ald_shutdown(NULL, 0);
568d28f42f9SLawrence Stewart 			mtx_destroy(&ald_mtx);
569d28f42f9SLawrence Stewart 		} else {
570d28f42f9SLawrence Stewart 			ALD_UNLOCK();
571d28f42f9SLawrence Stewart 			ret = EBUSY;
572d28f42f9SLawrence Stewart 		}
573d28f42f9SLawrence Stewart 		break;
574d28f42f9SLawrence Stewart 
575d28f42f9SLawrence Stewart 	case MOD_UNLOAD:
576d28f42f9SLawrence Stewart 		/* If MOD_QUIESCE failed we must fail here too. */
577d28f42f9SLawrence Stewart 		if (ald_shutingdown == 0)
578d28f42f9SLawrence Stewart 			ret = EBUSY;
579d28f42f9SLawrence Stewart 		break;
580d28f42f9SLawrence Stewart 
581d28f42f9SLawrence Stewart 	default:
582d28f42f9SLawrence Stewart 		ret = EINVAL;
583d28f42f9SLawrence Stewart 		break;
584d28f42f9SLawrence Stewart 	}
585d28f42f9SLawrence Stewart 
586d28f42f9SLawrence Stewart 	return (ret);
587d28f42f9SLawrence Stewart }
588d28f42f9SLawrence Stewart 
589d28f42f9SLawrence Stewart static moduledata_t alq_mod =
590d28f42f9SLawrence Stewart {
591d28f42f9SLawrence Stewart 	"alq",
592d28f42f9SLawrence Stewart 	alq_load_handler,
593d28f42f9SLawrence Stewart 	NULL
594d28f42f9SLawrence Stewart };
595d28f42f9SLawrence Stewart 
596d28f42f9SLawrence Stewart DECLARE_MODULE(alq, alq_mod, SI_SUB_SMP, SI_ORDER_ANY);
597d28f42f9SLawrence Stewart MODULE_VERSION(alq, 1);
598