aio_fsync(2): Support O_DSYNC.

aio_fsync(O_DSYNC, ...) is the asynchronous version of fdatasync(2).

Reviewed by: kib, asomers, jhb
Differential Review: https://reviews.freebsd.org/D25071
This commit is contained in:
Thomas Munro 2021-01-08 00:11:38 +13:00
parent e7347be9e3
commit 801ac943ea
4 changed files with 61 additions and 18 deletions

View File

@ -24,7 +24,7 @@
.\" .\"
.\" $FreeBSD$ .\" $FreeBSD$
.\" .\"
.Dd August 19, 2016 .Dd January 6, 2021
.Dt AIO_FSYNC 2 .Dt AIO_FSYNC 2
.Os .Os
.Sh NAME .Sh NAME
@ -49,11 +49,15 @@ completed at the time the call returns.
.Pp .Pp
The The
.Fa op .Fa op
argument can only be set to argument can be set to
.Dv O_SYNC .Dv O_SYNC
to cause all currently queued I/O operations to be completed to cause all currently queued I/O operations to be completed
as if by a call to as if by a call to
.Xr fsync 2 . .Xr fsync 2 ,
or
.Dv O_DSYNC
for the behavior of
.Xr fdatasync 2 .
.Pp .Pp
If _POSIX_PRIORITIZED_IO is defined, and the descriptor supports it, If _POSIX_PRIORITIZED_IO is defined, and the descriptor supports it,
then the enqueued operation is submitted at a priority equal to that then the enqueued operation is submitted at a priority equal to that
@ -112,7 +116,9 @@ are unsafe and unsafe asynchronous I/O operations are disabled.
A value of the A value of the
.Fa op .Fa op
argument is not set to argument is not set to
.Dv O_SYNC . .Dv O_SYNC
or
.Dv O_DSYNC .
.El .El
.Pp .Pp
The following conditions may be synchronously detected when the The following conditions may be synchronously detected when the
@ -176,3 +182,7 @@ The
.Fn aio_fsync .Fn aio_fsync
system call first appeared in system call first appeared in
.Fx 7.0 . .Fx 7.0 .
The
.Dv O_DSYNC
option appeared in
.Fx 13.0 .

View File

@ -718,10 +718,10 @@ aio_selectjob(struct aioproc *aiop)
/* /*
* Move all data to a permanent storage device. This code * Move all data to a permanent storage device. This code
* simulates the fsync syscall. * simulates the fsync and fdatasync syscalls.
*/ */
static int static int
aio_fsync_vnode(struct thread *td, struct vnode *vp) aio_fsync_vnode(struct thread *td, struct vnode *vp, int op)
{ {
struct mount *mp; struct mount *mp;
int error; int error;
@ -734,7 +734,10 @@ aio_fsync_vnode(struct thread *td, struct vnode *vp)
vm_object_page_clean(vp->v_object, 0, 0, 0); vm_object_page_clean(vp->v_object, 0, 0, 0);
VM_OBJECT_WUNLOCK(vp->v_object); VM_OBJECT_WUNLOCK(vp->v_object);
} }
error = VOP_FSYNC(vp, MNT_WAIT, td); if (op == LIO_DSYNC)
error = VOP_FDATASYNC(vp, td);
else
error = VOP_FSYNC(vp, MNT_WAIT, td);
VOP_UNLOCK(vp); VOP_UNLOCK(vp);
vn_finished_write(mp); vn_finished_write(mp);
@ -838,12 +841,15 @@ aio_process_sync(struct kaiocb *job)
struct file *fp = job->fd_file; struct file *fp = job->fd_file;
int error = 0; int error = 0;
KASSERT(job->uaiocb.aio_lio_opcode == LIO_SYNC, KASSERT(job->uaiocb.aio_lio_opcode == LIO_SYNC ||
job->uaiocb.aio_lio_opcode == LIO_DSYNC,
("%s: opcode %d", __func__, job->uaiocb.aio_lio_opcode)); ("%s: opcode %d", __func__, job->uaiocb.aio_lio_opcode));
td->td_ucred = job->cred; td->td_ucred = job->cred;
if (fp->f_vnode != NULL) if (fp->f_vnode != NULL) {
error = aio_fsync_vnode(td, fp->f_vnode); error = aio_fsync_vnode(td, fp->f_vnode,
job->uaiocb.aio_lio_opcode);
}
td->td_ucred = td_savedcred; td->td_ucred = td_savedcred;
if (error) if (error)
aio_complete(job, -1, error); aio_complete(job, -1, error);
@ -1579,6 +1585,7 @@ aio_aqueue(struct thread *td, struct aiocb *ujob, struct aioliojob *lj,
error = fget_read(td, fd, &cap_pread_rights, &fp); error = fget_read(td, fd, &cap_pread_rights, &fp);
break; break;
case LIO_SYNC: case LIO_SYNC:
case LIO_DSYNC:
error = fget(td, fd, &cap_fsync_rights, &fp); error = fget(td, fd, &cap_fsync_rights, &fp);
break; break;
case LIO_MLOCK: case LIO_MLOCK:
@ -1592,7 +1599,7 @@ aio_aqueue(struct thread *td, struct aiocb *ujob, struct aioliojob *lj,
if (error) if (error)
goto err3; goto err3;
if (opcode == LIO_SYNC && fp->f_vnode == NULL) { if ((opcode == LIO_SYNC || opcode == LIO_DSYNC) && fp->f_vnode == NULL) {
error = EINVAL; error = EINVAL;
goto err3; goto err3;
} }
@ -1805,10 +1812,12 @@ aio_queue_file(struct file *fp, struct kaiocb *job)
error = 0; error = 0;
break; break;
case LIO_SYNC: case LIO_SYNC:
case LIO_DSYNC:
AIO_LOCK(ki); AIO_LOCK(ki);
TAILQ_FOREACH(job2, &ki->kaio_jobqueue, plist) { TAILQ_FOREACH(job2, &ki->kaio_jobqueue, plist) {
if (job2->fd_file == job->fd_file && if (job2->fd_file == job->fd_file &&
job2->uaiocb.aio_lio_opcode != LIO_SYNC && job2->uaiocb.aio_lio_opcode != LIO_SYNC &&
job2->uaiocb.aio_lio_opcode != LIO_DSYNC &&
job2->seqno < job->seqno) { job2->seqno < job->seqno) {
job2->jobflags |= KAIOCB_CHECKSYNC; job2->jobflags |= KAIOCB_CHECKSYNC;
job->pending++; job->pending++;
@ -2587,10 +2596,20 @@ static int
kern_aio_fsync(struct thread *td, int op, struct aiocb *ujob, kern_aio_fsync(struct thread *td, int op, struct aiocb *ujob,
struct aiocb_ops *ops) struct aiocb_ops *ops)
{ {
int listop;
if (op != O_SYNC) /* XXX lack of O_DSYNC */ switch (op) {
case O_SYNC:
listop = LIO_SYNC;
break;
case O_DSYNC:
listop = LIO_DSYNC;
break;
default:
return (EINVAL); return (EINVAL);
return (aio_aqueue(td, ujob, NULL, LIO_SYNC, ops)); }
return (aio_aqueue(td, ujob, NULL, listop, ops));
} }
int int

View File

@ -48,6 +48,7 @@
#define LIO_MLOCK 0x4 #define LIO_MLOCK 0x4
#define LIO_WRITEV 0x5 #define LIO_WRITEV 0x5
#define LIO_READV 0x6 #define LIO_READV 0x6
#define LIO_DSYNC 0x7
#endif #endif
/* /*

View File

@ -1259,7 +1259,7 @@ ATF_TC_BODY(aio_fsync_errors, tc)
ATF_REQUIRE_MSG(fd != -1, "open failed: %s", strerror(errno)); ATF_REQUIRE_MSG(fd != -1, "open failed: %s", strerror(errno));
unlink(FILE_PATHNAME); unlink(FILE_PATHNAME);
/* aio_fsync should return EINVAL unless op is O_SYNC */ /* aio_fsync should return EINVAL unless op is O_SYNC or O_DSYNC */
memset(&iocb, 0, sizeof(iocb)); memset(&iocb, 0, sizeof(iocb));
iocb.aio_fildes = fd; iocb.aio_fildes = fd;
ATF_CHECK_EQ(-1, aio_fsync(666, &iocb)); ATF_CHECK_EQ(-1, aio_fsync(666, &iocb));
@ -1282,8 +1282,8 @@ ATF_TC_BODY(aio_fsync_errors, tc)
/* /*
* This test just performs a basic test of aio_fsync(). * This test just performs a basic test of aio_fsync().
*/ */
ATF_TC_WITHOUT_HEAD(aio_fsync_test); static void
ATF_TC_BODY(aio_fsync_test, tc) aio_fsync_test(int op)
{ {
struct aiocb synccb, *iocbp; struct aiocb synccb, *iocbp;
struct { struct {
@ -1328,7 +1328,7 @@ ATF_TC_BODY(aio_fsync_test, tc)
/* Queue the aio_fsync request. */ /* Queue the aio_fsync request. */
memset(&synccb, 0, sizeof(synccb)); memset(&synccb, 0, sizeof(synccb));
synccb.aio_fildes = fd; synccb.aio_fildes = fd;
ATF_REQUIRE(aio_fsync(O_SYNC, &synccb) == 0); ATF_REQUIRE(aio_fsync(op, &synccb) == 0);
/* Wait for requests to complete. */ /* Wait for requests to complete. */
for (;;) { for (;;) {
@ -1359,6 +1359,18 @@ ATF_TC_BODY(aio_fsync_test, tc)
close(fd); close(fd);
} }
ATF_TC_WITHOUT_HEAD(aio_fsync_sync_test);
ATF_TC_BODY(aio_fsync_sync_test, tc)
{
aio_fsync_test(O_SYNC);
}
ATF_TC_WITHOUT_HEAD(aio_fsync_dsync_test);
ATF_TC_BODY(aio_fsync_dsync_test, tc)
{
aio_fsync_test(O_DSYNC);
}
/* /*
* We shouldn't be able to DoS the system by setting iov_len to an insane * We shouldn't be able to DoS the system by setting iov_len to an insane
* value * value
@ -1782,7 +1794,8 @@ ATF_TP_ADD_TCS(tp)
ATF_TP_ADD_TC(tp, md_thread); ATF_TP_ADD_TC(tp, md_thread);
ATF_TP_ADD_TC(tp, md_waitcomplete); ATF_TP_ADD_TC(tp, md_waitcomplete);
ATF_TP_ADD_TC(tp, aio_fsync_errors); ATF_TP_ADD_TC(tp, aio_fsync_errors);
ATF_TP_ADD_TC(tp, aio_fsync_test); ATF_TP_ADD_TC(tp, aio_fsync_sync_test);
ATF_TP_ADD_TC(tp, aio_fsync_dsync_test);
ATF_TP_ADD_TC(tp, aio_large_read_test); ATF_TP_ADD_TC(tp, aio_large_read_test);
ATF_TP_ADD_TC(tp, aio_socket_two_reads); ATF_TP_ADD_TC(tp, aio_socket_two_reads);
ATF_TP_ADD_TC(tp, aio_socket_blocking_short_write); ATF_TP_ADD_TC(tp, aio_socket_blocking_short_write);