Fix errant EFAULT during writes (#8719)
Commit 98bb45e
resolved a deadlock which could occur when
handling a page fault in zfs_write(). This change added
the uio_fault_disable field to the uio structure but failed
to initialize it to B_FALSE. This uninitialized field would
cause uiomove_iov() to call __copy_from_user_inatomic()
instead of copy_from_user() resulting in unexpected EFAULTs.
Resolve the issue by fully initializing the uio, and clearing
the uio_fault_disable flags after it's used in zfs_write().
Additionally, reorder the uio_t field assignments to match
the order the fields are declared in the structure.
Reviewed-by: Chunwei Chen <tuxoko@gmail.com>
Reviewed-by: Richard Laager <rlaager@wiktel.com>
Reviewed-by: Tim Chase <tim@chase2k.com>
Signed-off-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #8640
Closes #8719
This commit is contained in:
parent
1f02ecc5a5
commit
515ddf6504
|
@ -822,6 +822,7 @@ zfs_write(struct inode *ip, uio_t *uio, int ioflag, cred_t *cr)
|
||||||
uio->uio_fault_disable = B_TRUE;
|
uio->uio_fault_disable = B_TRUE;
|
||||||
error = dmu_write_uio_dbuf(sa_get_db(zp->z_sa_hdl),
|
error = dmu_write_uio_dbuf(sa_get_db(zp->z_sa_hdl),
|
||||||
uio, nbytes, tx);
|
uio, nbytes, tx);
|
||||||
|
uio->uio_fault_disable = B_FALSE;
|
||||||
if (error == EFAULT) {
|
if (error == EFAULT) {
|
||||||
dmu_tx_commit(tx);
|
dmu_tx_commit(tx);
|
||||||
if (uio_prefaultpages(MIN(n, max_blksz), uio)) {
|
if (uio_prefaultpages(MIN(n, max_blksz), uio)) {
|
||||||
|
|
|
@ -246,17 +246,17 @@ zpl_read_common_iovec(struct inode *ip, const struct iovec *iovp, size_t count,
|
||||||
cred_t *cr, size_t skip)
|
cred_t *cr, size_t skip)
|
||||||
{
|
{
|
||||||
ssize_t read;
|
ssize_t read;
|
||||||
uio_t uio;
|
uio_t uio = { { 0 }, 0 };
|
||||||
int error;
|
int error;
|
||||||
fstrans_cookie_t cookie;
|
fstrans_cookie_t cookie;
|
||||||
|
|
||||||
uio.uio_iov = iovp;
|
uio.uio_iov = iovp;
|
||||||
uio.uio_skip = skip;
|
|
||||||
uio.uio_resid = count;
|
|
||||||
uio.uio_iovcnt = nr_segs;
|
uio.uio_iovcnt = nr_segs;
|
||||||
uio.uio_loffset = *ppos;
|
uio.uio_loffset = *ppos;
|
||||||
uio.uio_limit = MAXOFFSET_T;
|
|
||||||
uio.uio_segflg = segment;
|
uio.uio_segflg = segment;
|
||||||
|
uio.uio_limit = MAXOFFSET_T;
|
||||||
|
uio.uio_resid = count;
|
||||||
|
uio.uio_skip = skip;
|
||||||
|
|
||||||
cookie = spl_fstrans_mark();
|
cookie = spl_fstrans_mark();
|
||||||
error = -zfs_read(ip, &uio, flags, cr);
|
error = -zfs_read(ip, &uio, flags, cr);
|
||||||
|
@ -356,7 +356,7 @@ zpl_write_common_iovec(struct inode *ip, const struct iovec *iovp, size_t count,
|
||||||
cred_t *cr, size_t skip)
|
cred_t *cr, size_t skip)
|
||||||
{
|
{
|
||||||
ssize_t wrote;
|
ssize_t wrote;
|
||||||
uio_t uio;
|
uio_t uio = { { 0 }, 0 };
|
||||||
int error;
|
int error;
|
||||||
fstrans_cookie_t cookie;
|
fstrans_cookie_t cookie;
|
||||||
|
|
||||||
|
@ -364,12 +364,12 @@ zpl_write_common_iovec(struct inode *ip, const struct iovec *iovp, size_t count,
|
||||||
*ppos = i_size_read(ip);
|
*ppos = i_size_read(ip);
|
||||||
|
|
||||||
uio.uio_iov = iovp;
|
uio.uio_iov = iovp;
|
||||||
uio.uio_skip = skip;
|
|
||||||
uio.uio_resid = count;
|
|
||||||
uio.uio_iovcnt = nr_segs;
|
uio.uio_iovcnt = nr_segs;
|
||||||
uio.uio_loffset = *ppos;
|
uio.uio_loffset = *ppos;
|
||||||
uio.uio_limit = MAXOFFSET_T;
|
|
||||||
uio.uio_segflg = segment;
|
uio.uio_segflg = segment;
|
||||||
|
uio.uio_limit = MAXOFFSET_T;
|
||||||
|
uio.uio_resid = count;
|
||||||
|
uio.uio_skip = skip;
|
||||||
|
|
||||||
cookie = spl_fstrans_mark();
|
cookie = spl_fstrans_mark();
|
||||||
error = -zfs_write(ip, &uio, flags, cr);
|
error = -zfs_write(ip, &uio, flags, cr);
|
||||||
|
|
|
@ -493,7 +493,7 @@ zpl_get_link_common(struct dentry *dentry, struct inode *ip, char **link)
|
||||||
fstrans_cookie_t cookie;
|
fstrans_cookie_t cookie;
|
||||||
cred_t *cr = CRED();
|
cred_t *cr = CRED();
|
||||||
struct iovec iov;
|
struct iovec iov;
|
||||||
uio_t uio;
|
uio_t uio = { { 0 }, 0 };
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
crhold(cr);
|
crhold(cr);
|
||||||
|
@ -503,9 +503,8 @@ zpl_get_link_common(struct dentry *dentry, struct inode *ip, char **link)
|
||||||
|
|
||||||
uio.uio_iov = &iov;
|
uio.uio_iov = &iov;
|
||||||
uio.uio_iovcnt = 1;
|
uio.uio_iovcnt = 1;
|
||||||
uio.uio_skip = 0;
|
|
||||||
uio.uio_resid = (MAXPATHLEN - 1);
|
|
||||||
uio.uio_segflg = UIO_SYSSPACE;
|
uio.uio_segflg = UIO_SYSSPACE;
|
||||||
|
uio.uio_resid = (MAXPATHLEN - 1);
|
||||||
|
|
||||||
cookie = spl_fstrans_mark();
|
cookie = spl_fstrans_mark();
|
||||||
error = -zfs_readlink(ip, &uio, cr);
|
error = -zfs_readlink(ip, &uio, cr);
|
||||||
|
|
|
@ -727,12 +727,12 @@ static void
|
||||||
uio_from_bio(uio_t *uio, struct bio *bio)
|
uio_from_bio(uio_t *uio, struct bio *bio)
|
||||||
{
|
{
|
||||||
uio->uio_bvec = &bio->bi_io_vec[BIO_BI_IDX(bio)];
|
uio->uio_bvec = &bio->bi_io_vec[BIO_BI_IDX(bio)];
|
||||||
uio->uio_skip = BIO_BI_SKIP(bio);
|
|
||||||
uio->uio_resid = BIO_BI_SIZE(bio);
|
|
||||||
uio->uio_iovcnt = bio->bi_vcnt - BIO_BI_IDX(bio);
|
uio->uio_iovcnt = bio->bi_vcnt - BIO_BI_IDX(bio);
|
||||||
uio->uio_loffset = BIO_BI_SECTOR(bio) << 9;
|
uio->uio_loffset = BIO_BI_SECTOR(bio) << 9;
|
||||||
uio->uio_limit = MAXOFFSET_T;
|
|
||||||
uio->uio_segflg = UIO_BVEC;
|
uio->uio_segflg = UIO_BVEC;
|
||||||
|
uio->uio_limit = MAXOFFSET_T;
|
||||||
|
uio->uio_resid = BIO_BI_SIZE(bio);
|
||||||
|
uio->uio_skip = BIO_BI_SKIP(bio);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -742,7 +742,7 @@ zvol_write(void *arg)
|
||||||
|
|
||||||
zv_request_t *zvr = arg;
|
zv_request_t *zvr = arg;
|
||||||
struct bio *bio = zvr->bio;
|
struct bio *bio = zvr->bio;
|
||||||
uio_t uio;
|
uio_t uio = { { 0 }, 0 };
|
||||||
uio_from_bio(&uio, bio);
|
uio_from_bio(&uio, bio);
|
||||||
|
|
||||||
zvol_state_t *zv = zvr->zv;
|
zvol_state_t *zv = zvr->zv;
|
||||||
|
@ -897,7 +897,7 @@ zvol_read(void *arg)
|
||||||
|
|
||||||
zv_request_t *zvr = arg;
|
zv_request_t *zvr = arg;
|
||||||
struct bio *bio = zvr->bio;
|
struct bio *bio = zvr->bio;
|
||||||
uio_t uio;
|
uio_t uio = { { 0 }, 0 };
|
||||||
uio_from_bio(&uio, bio);
|
uio_from_bio(&uio, bio);
|
||||||
|
|
||||||
zvol_state_t *zv = zvr->zv;
|
zvol_state_t *zv = zvr->zv;
|
||||||
|
|
Loading…
Reference in New Issue