diff options
author | kib <kib@FreeBSD.org> | 2013-03-27 11:47:52 +0000 |
---|---|---|
committer | kib <kib@FreeBSD.org> | 2013-03-27 11:47:52 +0000 |
commit | c45e5da903f8140c2f15402b2424837677b648aa (patch) | |
tree | f96042ef528289a58a359ca052c0dab4f7c6e940 /sys/kern | |
parent | 448e7c12908c8a8680ec5f93934de43b8ad1a06f (diff) | |
download | FreeBSD-src-c45e5da903f8140c2f15402b2424837677b648aa.zip FreeBSD-src-c45e5da903f8140c2f15402b2424837677b648aa.tar.gz |
Fix a race with the vnode reclamation in the aio_qphysio(). Obtain
the thread reference on the vp->v_rdev and use the returned struct
cdev *dev instead of using vp->v_rdev. Call dev_strategy_csw()
instead of dev_strategy(), since we now own the reference.
Since the csw was already calculated, test d_flags to avoid mapping
the buffer if the driver supports unmapped requests [*].
Suggested by: kan [*]
Reviewed by: kan (previous version)
Sponsored by: The FreeBSD Foundation
MFC after: 2 weeks
Diffstat (limited to 'sys/kern')
-rw-r--r-- | sys/kern/vfs_aio.c | 23 |
1 files changed, 17 insertions, 6 deletions
diff --git a/sys/kern/vfs_aio.c b/sys/kern/vfs_aio.c index 4facd97..3ef1085 100644 --- a/sys/kern/vfs_aio.c +++ b/sys/kern/vfs_aio.c @@ -1252,9 +1252,11 @@ aio_qphysio(struct proc *p, struct aiocblist *aiocbe) struct file *fp; struct buf *bp; struct vnode *vp; + struct cdevsw *csw; + struct cdev *dev; struct kaioinfo *ki; struct aioliojob *lj; - int error; + int error, ref; cb = &aiocbe->uaiocb; fp = aiocbe->fd_file; @@ -1282,9 +1284,6 @@ aio_qphysio(struct proc *p, struct aiocblist *aiocbe) if (cb->aio_nbytes % vp->v_bufobj.bo_bsize) return (-1); - if (cb->aio_nbytes > vp->v_rdev->si_iosize_max) - return (-1); - if (cb->aio_nbytes > MAXPHYS - (((vm_offset_t) cb->aio_buf) & PAGE_MASK)) return (-1); @@ -1293,6 +1292,15 @@ aio_qphysio(struct proc *p, struct aiocblist *aiocbe) if (ki->kaio_buffer_count >= ki->kaio_ballowed_count) return (-1); + ref = 0; + csw = devvn_refthread(vp, &dev, &ref); + if (csw == NULL) + return (ENXIO); + if (cb->aio_nbytes > dev->si_iosize_max) { + error = -1; + goto unref; + } + /* Create and build a buffer header for a transfer. */ bp = (struct buf *)getpbuf(NULL); BUF_KERNPROC(bp); @@ -1323,7 +1331,7 @@ aio_qphysio(struct proc *p, struct aiocblist *aiocbe) /* * Bring buffer into kernel space. */ - if (vmapbuf(bp, 1) < 0) { + if (vmapbuf(bp, (csw->d_flags & D_UNMAPPED_IO) == 0) < 0) { error = EFAULT; goto doerror; } @@ -1345,7 +1353,8 @@ aio_qphysio(struct proc *p, struct aiocblist *aiocbe) TASK_INIT(&aiocbe->biotask, 0, biohelper, aiocbe); /* Perform transfer. */ - dev_strategy(vp->v_rdev, bp); + dev_strategy_csw(dev, csw, bp); + dev_relthread(dev, ref); return (0); doerror: @@ -1357,6 +1366,8 @@ doerror: aiocbe->bp = NULL; AIO_UNLOCK(ki); relpbuf(bp, NULL); +unref: + dev_relthread(dev, ref); return (error); } |