Commit b1eaf041 authored by Christoph Hellwig's avatar Christoph Hellwig

XFS: Implement readv/writev

Modid: 2.5.x-xfs:slinx:128366a
parent 20cebe45
...@@ -40,70 +40,76 @@ static struct vm_operations_struct linvfs_file_vm_ops; ...@@ -40,70 +40,76 @@ static struct vm_operations_struct linvfs_file_vm_ops;
STATIC ssize_t STATIC ssize_t
linvfs_read( linvfs_readv(
struct file *filp, struct file *filp,
char *buf, const struct iovec *iovp,
size_t size, unsigned long nr_segs,
loff_t *offset) loff_t *ppos)
{ {
vnode_t *vp; vnode_t *vp = LINVFS_GET_VP(filp->f_dentry->d_inode);
int error; int error;
vp = LINVFS_GET_VP(filp->f_dentry->d_inode); VOP_READ(vp, filp, iovp, nr_segs, ppos, NULL, error);
ASSERT(vp);
VOP_READ(vp, filp, buf, size, offset, NULL, error);
return(error); return error;
} }
STATIC ssize_t STATIC ssize_t
linvfs_write( linvfs_writev(
struct file *file, struct file *filp,
const char *buf, const struct iovec *iovp,
size_t count, unsigned long nr_segs,
loff_t *ppos) loff_t *ppos)
{ {
struct inode *inode = file->f_dentry->d_inode; struct inode *inode = filp->f_dentry->d_inode;
loff_t pos; vnode_t *vp = LINVFS_GET_VP(inode);
vnode_t *vp; int error = filp->f_error;
int err; /* Use negative errors in this f'n */
if ((ssize_t) count < 0)
return -EINVAL;
if (!access_ok(VERIFY_READ, buf, count))
return -EFAULT;
pos = *ppos;
err = -EINVAL;
if (pos < 0)
goto out;
err = file->f_error; if (unlikely(error)) {
if (err) { filp->f_error = 0;
file->f_error = 0; return error;
goto out;
} }
vp = LINVFS_GET_VP(inode); /*
ASSERT(vp); * We allow multiple direct writers in, there is no
/* We allow multiple direct writers in, there is no
* potential call to vmtruncate in that path. * potential call to vmtruncate in that path.
*/ */
if (!(file->f_flags & O_DIRECT)) if (filp->f_flags & O_DIRECT) {
VOP_WRITE(vp, filp, iovp, nr_segs, ppos, NULL, error);
} else {
down(&inode->i_sem); down(&inode->i_sem);
VOP_WRITE(vp, filp, iovp, nr_segs, ppos, NULL, error);
up(&inode->i_sem);
}
VOP_WRITE(vp, file, buf, count, &pos, NULL, err); return error;
*ppos = pos; }
if (!(file->f_flags & O_DIRECT))
up(&inode->i_sem);
out:
return(err); STATIC ssize_t
linvfs_read(
struct file *filp,
char *buf,
size_t count,
loff_t *ppos)
{
struct iovec iov = {buf, count};
return linvfs_readv(filp, &iov, 1, ppos);
}
STATIC ssize_t
linvfs_write(
struct file *file,
const char *buf,
size_t count,
loff_t *ppos)
{
struct iovec iov = {(void *)buf, count};
return linvfs_writev(file, &iov, 1, ppos);
} }
...@@ -312,6 +318,8 @@ struct file_operations linvfs_file_operations = { ...@@ -312,6 +318,8 @@ struct file_operations linvfs_file_operations = {
.llseek = generic_file_llseek, .llseek = generic_file_llseek,
.read = linvfs_read, .read = linvfs_read,
.write = linvfs_write, .write = linvfs_write,
.readv = linvfs_readv,
.writev = linvfs_writev,
.ioctl = linvfs_ioctl, .ioctl = linvfs_ioctl,
.mmap = linvfs_file_mmap, .mmap = linvfs_file_mmap,
.open = linvfs_open, .open = linvfs_open,
......
...@@ -125,35 +125,63 @@ xfs_iozero( ...@@ -125,35 +125,63 @@ xfs_iozero(
ssize_t /* bytes read, or (-) error */ ssize_t /* bytes read, or (-) error */
xfs_read( xfs_read(
bhv_desc_t *bdp, bhv_desc_t *bdp,
struct file *file, struct file *filp,
char *buf, const struct iovec *iovp,
size_t size, unsigned long segs,
loff_t *offset, loff_t *offp,
cred_t *credp) cred_t *credp)
{ {
size_t size = 0;
ssize_t ret; ssize_t ret;
xfs_fsize_t n; xfs_fsize_t n;
xfs_inode_t *ip; xfs_inode_t *ip;
xfs_mount_t *mp; xfs_mount_t *mp;
unsigned long seg;
int direct = filp->f_flags & O_DIRECT;
ip = XFS_BHVTOI(bdp); ip = XFS_BHVTOI(bdp);
mp = ip->i_mount; mp = ip->i_mount;
XFS_STATS_INC(xfsstats.xs_read_calls); XFS_STATS_INC(xfsstats.xs_read_calls);
if (file->f_flags & O_DIRECT) { /* START copy & waste from filemap.c */
if (((__psint_t)buf & BBMASK) || for (seg = 0; seg < segs; seg++) {
(*offset & mp->m_blockmask) || const struct iovec *iv = &iovp[seg];
/*
* If any segment has a negative length, or the cumulative
* length ever wraps negative then return -EINVAL.
*/
size += iv->iov_len;
if (unlikely((ssize_t)(size|iv->iov_len) < 0))
return XFS_ERROR(-EINVAL);
if (direct) { /* XFS specific check */
if ((__psint_t)iv->iov_base & BBMASK) {
if (*offp == ip->i_d.di_size)
return 0;
return XFS_ERROR(-EINVAL);
}
}
if (access_ok(VERIFY_WRITE, iv->iov_base, iv->iov_len))
continue;
if (seg == 0)
return XFS_ERROR(-EFAULT);
segs = seg;
break;
}
/* END copy & waste from filemap.c */
if (direct) {
if ((*offp & mp->m_blockmask) ||
(size & mp->m_blockmask)) { (size & mp->m_blockmask)) {
if (*offset == ip->i_d.di_size) { if (*offp == ip->i_d.di_size) {
return (0); return (0);
} }
return -XFS_ERROR(EINVAL); return -XFS_ERROR(EINVAL);
} }
} }
n = XFS_MAX_FILE_OFFSET - *offp;
n = XFS_MAX_FILE_OFFSET - *offset;
if ((n <= 0) || (size == 0)) if ((n <= 0) || (size == 0))
return 0; return 0;
...@@ -167,26 +195,24 @@ xfs_read( ...@@ -167,26 +195,24 @@ xfs_read(
xfs_ilock(ip, XFS_IOLOCK_SHARED); xfs_ilock(ip, XFS_IOLOCK_SHARED);
if (DM_EVENT_ENABLED(BHV_TO_VNODE(bdp)->v_vfsp, ip, DM_EVENT_READ) && if (DM_EVENT_ENABLED(BHV_TO_VNODE(bdp)->v_vfsp, ip, DM_EVENT_READ) &&
!(file->f_mode & FINVIS)) { !(filp->f_mode & FINVIS)) {
int error; int error;
vrwlock_t locktype = VRWLOCK_READ; vrwlock_t locktype = VRWLOCK_READ;
error = xfs_dm_send_data_event(DM_EVENT_READ, bdp, error = xfs_dm_send_data_event(DM_EVENT_READ, bdp, *offp,
*offset, size, size, FILP_DELAY_FLAG(filp), &locktype);
FILP_DELAY_FLAG(file),
&locktype);
if (error) { if (error) {
xfs_iunlock(ip, XFS_IOLOCK_SHARED); xfs_iunlock(ip, XFS_IOLOCK_SHARED);
return -error; return -error;
} }
} }
ret = generic_file_read(file, buf, size, offset); ret = generic_file_readv(filp, iovp, segs, offp);
xfs_iunlock(ip, XFS_IOLOCK_SHARED); xfs_iunlock(ip, XFS_IOLOCK_SHARED);
XFS_STATS_ADD(xfsstats.xs_read_bytes, ret); XFS_STATS_ADD(xfsstats.xs_read_bytes, ret);
if (!(file->f_mode & FINVIS)) if (!(filp->f_mode & FINVIS))
xfs_ichgtime(ip, XFS_ICHGTIME_ACC); xfs_ichgtime(ip, XFS_ICHGTIME_ACC);
return ret; return ret;
...@@ -420,11 +446,12 @@ ssize_t /* bytes written, or (-) error */ ...@@ -420,11 +446,12 @@ ssize_t /* bytes written, or (-) error */
xfs_write( xfs_write(
bhv_desc_t *bdp, bhv_desc_t *bdp,
struct file *file, struct file *file,
const char *buf, const struct iovec *iovp,
size_t size, unsigned long segs,
loff_t *offset, loff_t *offset,
cred_t *credp) cred_t *credp)
{ {
size_t size = 0;
xfs_inode_t *xip; xfs_inode_t *xip;
xfs_mount_t *mp; xfs_mount_t *mp;
ssize_t ret; ssize_t ret;
...@@ -433,7 +460,7 @@ xfs_write( ...@@ -433,7 +460,7 @@ xfs_write(
xfs_fsize_t n, limit = XFS_MAX_FILE_OFFSET; xfs_fsize_t n, limit = XFS_MAX_FILE_OFFSET;
xfs_iocore_t *io; xfs_iocore_t *io;
vnode_t *vp; vnode_t *vp;
struct iovec iov; unsigned long seg;
int iolock; int iolock;
int direct = file->f_flags & O_DIRECT; int direct = file->f_flags & O_DIRECT;
int eventsent = 0; int eventsent = 0;
...@@ -444,6 +471,30 @@ xfs_write( ...@@ -444,6 +471,30 @@ xfs_write(
vp = BHV_TO_VNODE(bdp); vp = BHV_TO_VNODE(bdp);
xip = XFS_BHVTOI(bdp); xip = XFS_BHVTOI(bdp);
/* START copy & waste from filemap.c */
for (seg = 0; seg < segs; seg++) {
const struct iovec *iv = &iovp[seg];
/*
* If any segment has a negative length, or the cumulative
* length ever wraps negative then return -EINVAL.
*/
size += iv->iov_len;
if (unlikely((ssize_t)(size|iv->iov_len) < 0))
return XFS_ERROR(-EINVAL);
if (direct) { /* XFS specific check */
if ((__psint_t)iv->iov_base & BBMASK)
return XFS_ERROR(-EINVAL);
}
if (access_ok(VERIFY_READ, iv->iov_base, iv->iov_len))
continue;
if (seg == 0)
return XFS_ERROR(-EFAULT);
segs = seg;
break;
}
/* END copy & waste from filemap.c */
if (size == 0) if (size == 0)
return 0; return 0;
...@@ -457,8 +508,7 @@ xfs_write( ...@@ -457,8 +508,7 @@ xfs_write(
} }
if (direct) { if (direct) {
if (((__psint_t)buf & BBMASK) || if ((*offset & mp->m_blockmask) ||
(*offset & mp->m_blockmask) ||
(size & mp->m_blockmask)) { (size & mp->m_blockmask)) {
return XFS_ERROR(-EINVAL); return XFS_ERROR(-EINVAL);
} }
...@@ -481,6 +531,7 @@ xfs_write( ...@@ -481,6 +531,7 @@ xfs_write(
xfs_iunlock(xip, XFS_ILOCK_EXCL|iolock); xfs_iunlock(xip, XFS_ILOCK_EXCL|iolock);
return -EFBIG; return -EFBIG;
} }
if (n < size) if (n < size)
size = n; size = n;
...@@ -572,10 +623,7 @@ xfs_write( ...@@ -572,10 +623,7 @@ xfs_write(
xfs_inval_cached_pages(vp, &xip->i_iocore, *offset, 1, 1); xfs_inval_cached_pages(vp, &xip->i_iocore, *offset, 1, 1);
} }
iov.iov_base = (void *)buf; ret = generic_file_write_nolock(file, iovp, segs, offset);
iov.iov_len = size;
ret = generic_file_write_nolock(file, &iov, 1, offset);
if ((ret == -ENOSPC) && if ((ret == -ENOSPC) &&
DM_EVENT_ENABLED(vp->v_vfsp, xip, DM_EVENT_NOSPACE) && DM_EVENT_ENABLED(vp->v_vfsp, xip, DM_EVENT_NOSPACE) &&
......
...@@ -48,18 +48,18 @@ extern int xfs_zero_eof (vnode_t *, struct xfs_iocore *, xfs_off_t, ...@@ -48,18 +48,18 @@ extern int xfs_zero_eof (vnode_t *, struct xfs_iocore *, xfs_off_t,
xfs_fsize_t, xfs_fsize_t, struct pm *); xfs_fsize_t, xfs_fsize_t, struct pm *);
extern ssize_t xfs_read ( extern ssize_t xfs_read (
struct bhv_desc *bdp, struct bhv_desc *bdp,
struct file *file, struct file *filp,
char *buf, const struct iovec *iovp,
size_t size, unsigned long segs,
loff_t *offset, loff_t *offp,
struct cred *credp); struct cred *credp);
extern ssize_t xfs_write ( extern ssize_t xfs_write (
struct bhv_desc *bdp, struct bhv_desc *bdp,
struct file *file, struct file *filp,
const char *buf, const struct iovec *iovp,
size_t size, unsigned long segs,
loff_t *offset, loff_t *offp,
struct cred *credp); struct cred *credp);
extern int xfs_recover_read_only (xlog_t *); extern int xfs_recover_read_only (xlog_t *);
......
...@@ -175,9 +175,11 @@ struct page_buf_bmap_s; ...@@ -175,9 +175,11 @@ struct page_buf_bmap_s;
struct attrlist_cursor_kern; struct attrlist_cursor_kern;
typedef int (*vop_open_t)(bhv_desc_t *, struct cred *); typedef int (*vop_open_t)(bhv_desc_t *, struct cred *);
typedef ssize_t (*vop_read_t)(bhv_desc_t *, struct file *, char *, size_t, typedef ssize_t (*vop_read_t)(bhv_desc_t *, struct file *,
const struct iovec *, unsigned long,
loff_t *, struct cred *); loff_t *, struct cred *);
typedef ssize_t (*vop_write_t)(bhv_desc_t *, struct file *, const char *, size_t, typedef ssize_t (*vop_write_t)(bhv_desc_t *, struct file *,
const struct iovec *, unsigned long,
loff_t *, struct cred *); loff_t *, struct cred *);
typedef int (*vop_ioctl_t)(bhv_desc_t *, struct inode *, struct file *, unsigned int, unsigned long); typedef int (*vop_ioctl_t)(bhv_desc_t *, struct inode *, struct file *, unsigned int, unsigned long);
typedef int (*vop_getattr_t)(bhv_desc_t *, struct vattr *, int, typedef int (*vop_getattr_t)(bhv_desc_t *, struct vattr *, int,
...@@ -272,16 +274,16 @@ typedef struct vnodeops { ...@@ -272,16 +274,16 @@ typedef struct vnodeops {
*/ */
#define _VOP_(op, vp) (*((vnodeops_t *)(vp)->v_fops)->op) #define _VOP_(op, vp) (*((vnodeops_t *)(vp)->v_fops)->op)
#define VOP_READ(vp,file,buf,size,offset,cr,rv) \ #define VOP_READ(vp,file,iov,segs,offset,cr,rv) \
{ \ { \
VN_BHV_READ_LOCK(&(vp)->v_bh); \ VN_BHV_READ_LOCK(&(vp)->v_bh); \
rv = _VOP_(vop_read, vp)((vp)->v_fbhv,file,buf,size,offset,cr); \ rv = _VOP_(vop_read, vp)((vp)->v_fbhv,file,iov,segs,offset,cr); \
VN_BHV_READ_UNLOCK(&(vp)->v_bh); \ VN_BHV_READ_UNLOCK(&(vp)->v_bh); \
} }
#define VOP_WRITE(vp,file,buf,size,offset,cr,rv) \ #define VOP_WRITE(vp,file,iov,segs,offset,cr,rv) \
{ \ { \
VN_BHV_READ_LOCK(&(vp)->v_bh); \ VN_BHV_READ_LOCK(&(vp)->v_bh); \
rv = _VOP_(vop_write, vp)((vp)->v_fbhv,file,buf,size,offset,cr);\ rv = _VOP_(vop_write, vp)((vp)->v_fbhv,file,iov,segs,offset,cr);\
VN_BHV_READ_UNLOCK(&(vp)->v_bh); \ VN_BHV_READ_UNLOCK(&(vp)->v_bh); \
} }
#define VOP_BMAP(vp,of,sz,rw,cr,b,n,rv) \ #define VOP_BMAP(vp,of,sz,rw,cr,b,n,rv) \
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment