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;
STATIC ssize_t
linvfs_read(
struct file *filp,
char *buf,
size_t size,
loff_t *offset)
linvfs_readv(
struct file *filp,
const struct iovec *iovp,
unsigned long nr_segs,
loff_t *ppos)
{
vnode_t *vp;
int error;
vnode_t *vp = LINVFS_GET_VP(filp->f_dentry->d_inode);
int error;
vp = LINVFS_GET_VP(filp->f_dentry->d_inode);
ASSERT(vp);
VOP_READ(vp, filp, iovp, nr_segs, ppos, NULL, error);
VOP_READ(vp, filp, buf, size, offset, NULL, error);
return(error);
return error;
}
STATIC ssize_t
linvfs_write(
struct file *file,
const char *buf,
size_t count,
loff_t *ppos)
linvfs_writev(
struct file *filp,
const struct iovec *iovp,
unsigned long nr_segs,
loff_t *ppos)
{
struct inode *inode = file->f_dentry->d_inode;
loff_t pos;
vnode_t *vp;
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;
struct inode *inode = filp->f_dentry->d_inode;
vnode_t *vp = LINVFS_GET_VP(inode);
int error = filp->f_error;
pos = *ppos;
err = -EINVAL;
if (pos < 0)
goto out;
err = file->f_error;
if (err) {
file->f_error = 0;
goto out;
if (unlikely(error)) {
filp->f_error = 0;
return error;
}
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.
*/
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);
VOP_WRITE(vp, filp, iovp, nr_segs, ppos, NULL, error);
up(&inode->i_sem);
}
VOP_WRITE(vp, file, buf, count, &pos, NULL, err);
*ppos = pos;
return error;
}
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 = {
.llseek = generic_file_llseek,
.read = linvfs_read,
.write = linvfs_write,
.readv = linvfs_readv,
.writev = linvfs_writev,
.ioctl = linvfs_ioctl,
.mmap = linvfs_file_mmap,
.open = linvfs_open,
......
......@@ -124,36 +124,64 @@ xfs_iozero(
ssize_t /* bytes read, or (-) error */
xfs_read(
bhv_desc_t *bdp,
struct file *file,
char *buf,
size_t size,
loff_t *offset,
cred_t *credp)
bhv_desc_t *bdp,
struct file *filp,
const struct iovec *iovp,
unsigned long segs,
loff_t *offp,
cred_t *credp)
{
ssize_t ret;
xfs_fsize_t n;
xfs_inode_t *ip;
xfs_mount_t *mp;
size_t size = 0;
ssize_t ret;
xfs_fsize_t n;
xfs_inode_t *ip;
xfs_mount_t *mp;
unsigned long seg;
int direct = filp->f_flags & O_DIRECT;
ip = XFS_BHVTOI(bdp);
mp = ip->i_mount;
XFS_STATS_INC(xfsstats.xs_read_calls);
if (file->f_flags & O_DIRECT) {
if (((__psint_t)buf & BBMASK) ||
(*offset & mp->m_blockmask) ||
/* 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) {
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)) {
if (*offset == ip->i_d.di_size) {
if (*offp == ip->i_d.di_size) {
return (0);
}
return -XFS_ERROR(EINVAL);
}
}
n = XFS_MAX_FILE_OFFSET - *offset;
n = XFS_MAX_FILE_OFFSET - *offp;
if ((n <= 0) || (size == 0))
return 0;
......@@ -167,26 +195,24 @@ xfs_read(
xfs_ilock(ip, XFS_IOLOCK_SHARED);
if (DM_EVENT_ENABLED(BHV_TO_VNODE(bdp)->v_vfsp, ip, DM_EVENT_READ) &&
!(file->f_mode & FINVIS)) {
!(filp->f_mode & FINVIS)) {
int error;
vrwlock_t locktype = VRWLOCK_READ;
error = xfs_dm_send_data_event(DM_EVENT_READ, bdp,
*offset, size,
FILP_DELAY_FLAG(file),
&locktype);
error = xfs_dm_send_data_event(DM_EVENT_READ, bdp, *offp,
size, FILP_DELAY_FLAG(filp), &locktype);
if (error) {
xfs_iunlock(ip, XFS_IOLOCK_SHARED);
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_STATS_ADD(xfsstats.xs_read_bytes, ret);
if (!(file->f_mode & FINVIS))
if (!(filp->f_mode & FINVIS))
xfs_ichgtime(ip, XFS_ICHGTIME_ACC);
return ret;
......@@ -418,32 +444,57 @@ xfs_zero_eof(
ssize_t /* bytes written, or (-) error */
xfs_write(
bhv_desc_t *bdp,
struct file *file,
const char *buf,
size_t size,
loff_t *offset,
cred_t *credp)
bhv_desc_t *bdp,
struct file *file,
const struct iovec *iovp,
unsigned long segs,
loff_t *offset,
cred_t *credp)
{
xfs_inode_t *xip;
xfs_mount_t *mp;
ssize_t ret;
int error = 0;
xfs_fsize_t isize, new_size;
xfs_fsize_t n, limit = XFS_MAX_FILE_OFFSET;
xfs_iocore_t *io;
vnode_t *vp;
struct iovec iov;
int iolock;
int direct = file->f_flags & O_DIRECT;
int eventsent = 0;
vrwlock_t locktype;
size_t size = 0;
xfs_inode_t *xip;
xfs_mount_t *mp;
ssize_t ret;
int error = 0;
xfs_fsize_t isize, new_size;
xfs_fsize_t n, limit = XFS_MAX_FILE_OFFSET;
xfs_iocore_t *io;
vnode_t *vp;
unsigned long seg;
int iolock;
int direct = file->f_flags & O_DIRECT;
int eventsent = 0;
vrwlock_t locktype;
XFS_STATS_INC(xfsstats.xs_write_calls);
vp = BHV_TO_VNODE(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)
return 0;
......@@ -457,9 +508,8 @@ xfs_write(
}
if (direct) {
if (((__psint_t)buf & BBMASK) ||
(*offset & mp->m_blockmask) ||
(size & mp->m_blockmask)) {
if ((*offset & mp->m_blockmask) ||
(size & mp->m_blockmask)) {
return XFS_ERROR(-EINVAL);
}
iolock = XFS_IOLOCK_SHARED;
......@@ -481,6 +531,7 @@ xfs_write(
xfs_iunlock(xip, XFS_ILOCK_EXCL|iolock);
return -EFBIG;
}
if (n < size)
size = n;
......@@ -572,10 +623,7 @@ xfs_write(
xfs_inval_cached_pages(vp, &xip->i_iocore, *offset, 1, 1);
}
iov.iov_base = (void *)buf;
iov.iov_len = size;
ret = generic_file_write_nolock(file, &iov, 1, offset);
ret = generic_file_write_nolock(file, iovp, segs, offset);
if ((ret == -ENOSPC) &&
DM_EVENT_ENABLED(vp->v_vfsp, xip, DM_EVENT_NOSPACE) &&
......
......@@ -47,19 +47,19 @@ extern int xfs_bdstrat_cb (struct xfs_buf *);
extern int xfs_zero_eof (vnode_t *, struct xfs_iocore *, xfs_off_t,
xfs_fsize_t, xfs_fsize_t, struct pm *);
extern ssize_t xfs_read (
struct bhv_desc *bdp,
struct file *file,
char *buf,
size_t size,
loff_t *offset,
struct cred *credp);
struct bhv_desc *bdp,
struct file *filp,
const struct iovec *iovp,
unsigned long segs,
loff_t *offp,
struct cred *credp);
extern ssize_t xfs_write (
struct bhv_desc *bdp,
struct file *file,
const char *buf,
size_t size,
loff_t *offset,
struct file *filp,
const struct iovec *iovp,
unsigned long segs,
loff_t *offp,
struct cred *credp);
extern int xfs_recover_read_only (xlog_t *);
......
......@@ -175,9 +175,11 @@ struct page_buf_bmap_s;
struct attrlist_cursor_kern;
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 *);
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 *);
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,
......@@ -272,16 +274,16 @@ typedef struct vnodeops {
*/
#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); \
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); \
}
#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); \
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); \
}
#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