Skip to contentSkip to conversation

iovec in ->aio_read/->aio_write

RFC In review Christoph Hellwig 4 participants 6 messages Oct 15, 2002 – Oct 17, 2002

Subsystems (7)
lore
Share & tools
lore
#0 lore raw

RFC 1/1 iovec in ->aio_read/->aio_write

I've recently looked into implementing the aio read and write methods for XFS. Although all of read/write readv/writev and aio_read/aio_write end up calling the exactly same code in filemap.c (for the generic filesystem I/O code). Filesystems like XFS that need additional code before calling the generic functionality have to duplicated it though.

I don't think it makes sense to keep all those interfaces. As the read/write entry points are used by most drivers I suggest starting with the other two, that have far less users. The patch below (compiled but not booted!) changes the aio_read/ aio_write interface to take the same array of iovec like readv/write and updates all users. Note that we don't support vectored I/O for the aio interface yet, but it seems like a logical addition to me.

Proposed next steps: Convert over all readv/writev users to aio_read/aio_write and remove the methods. Implement aio_read/aio_write in all filesystems using the generic pagecache code and kill the "normal" generic_file_read and generic_file_write.

Comments?

fs/aio.c +24 -2
@@ -987,6 +987,28 @@
 	return -EINVAL;
 }
 
+ssize_t aio_read_single(struct kiocb *iocb, char *buf,
+			size_t count, loff_t pos)
+{
+	struct file *file = iocb->ki_filp;
+
+	iocb->ki_single.iov_base = buf;
+	iocb->ki_single.iov_len = count;
+
+	return file->f_op->aio_read(iocb, &iocb->ki_single, 1, iocb->ki_pos);
+}
+
+ssize_t aio_write_single(struct kiocb *iocb, const char *buf,
+			 size_t count, loff_t pos)
+{
+	struct file *file = iocb->ki_filp;
+
+	iocb->ki_single.iov_base = (void *)buf;
+	iocb->ki_single.iov_len = count;
+
+	return file->f_op->aio_write(iocb, &iocb->ki_single, 1, iocb->ki_pos);
+}
+
 static int FASTCALL(io_submit_one(struct kioctx *ctx, struct iocb *user_iocb,
 				  struct iocb *iocb));
 static int io_submit_one(struct kioctx *ctx, struct iocb *user_iocb,
@@ -1048,7 +1070,7 @@
 			goto out_put_req;
 		ret = -EINVAL;
 		if (file->f_op->aio_read)
-			ret = file->f_op->aio_read(req, buf,
+			ret = aio_read_single(req, buf,
 					iocb->aio_nbytes, req->ki_pos);
 		break;
 	case IOCB_CMD_PWRITE:
@@ -1060,7 +1082,7 @@
 			goto out_put_req;
 		ret = -EINVAL;
 		if (file->f_op->aio_write)
-			ret = file->f_op->aio_write(req, buf,
+			ret = aio_write_single(req, buf,
 					iocb->aio_nbytes, req->ki_pos);
 		break;
 	case IOCB_CMD_FDSYNC:
fs/read_write.c +33 -2
@@ -184,7 +184,7 @@
 
 	init_sync_kiocb(&kiocb, filp);
 	kiocb.ki_pos = *ppos;
-	ret = filp->f_op->aio_read(&kiocb, buf, len, kiocb.ki_pos);
+	ret = aio_read_single(&kiocb, buf, len, kiocb.ki_pos);
 	if (-EIOCBQUEUED == ret)
 		ret = wait_on_sync_kiocb(&kiocb);
 	*ppos = kiocb.ki_pos;
@@ -224,7 +224,7 @@
 
 	init_sync_kiocb(&kiocb, filp);
 	kiocb.ki_pos = *ppos;
-	ret = filp->f_op->aio_write(&kiocb, buf, len, kiocb.ki_pos);
+	ret = aio_write_single(&kiocb, buf, len, kiocb.ki_pos);
 	if (-EIOCBQUEUED == ret)
 		ret = wait_on_sync_kiocb(&kiocb);
 	*ppos = kiocb.ki_pos;
@@ -340,6 +340,37 @@
 	}
 	return seg;
 }
+
+ssize_t do_sync_writev(struct file *filp, const struct iovec *iov,
+		unsigned long nr_segs, loff_t *ppos)
+{
+	struct kiocb kiocb;
+	ssize_t ret;
+
+	init_sync_kiocb(&kiocb, filp);
+	kiocb.ki_pos = *ppos;
+	ret = filp->f_op->aio_write(&kiocb, iov, nr_segs, kiocb.ki_pos);
+	if (-EIOCBQUEUED == ret)
+		ret = wait_on_sync_kiocb(&kiocb);
+	*ppos = kiocb.ki_pos;
+	return ret;
+}
+
+ssize_t do_sync_readv(struct file *filp, const struct iovec *iov,
+		unsigned long nr_segs, loff_t *ppos)
+{
+	struct kiocb kiocb;
+	ssize_t ret;
+
+	init_sync_kiocb(&kiocb, filp);
+	kiocb.ki_pos = *ppos;
+	ret = filp->f_op->aio_write(&kiocb, iov, nr_segs, kiocb.ki_pos);
+	if (-EIOCBQUEUED == ret)
+		ret = wait_on_sync_kiocb(&kiocb);
+	*ppos = kiocb.ki_pos;
+	return ret;
+}
+
 
 static ssize_t do_readv_writev(int type, struct file *file,
 			       const struct iovec * vector,
fs/ext3/file.c +5 -4
@@ -61,7 +61,8 @@
  */
 
 static ssize_t
-ext3_file_write(struct kiocb *iocb, const char *buf, size_t count, loff_t pos)
+ext3_file_aio_write(struct kiocb *iocb, const struct iovec *iov,
+		unsigned long nr_segs, loff_t pos)
 {
 	struct file *file = iocb->ki_filp;
 	struct inode *inode = file->f_dentry->d_inode;
@@ -76,15 +77,15 @@
 	if (IS_SYNC(inode) || (file->f_flags & O_SYNC))
 		mark_inode_dirty(inode);
 
-	return generic_file_aio_write(iocb, buf, count, pos);
+	return generic_file_aio_write(iocb, iov, nr_segs, pos);
 }
 
 struct file_operations ext3_file_operations = {
 	.llseek		= generic_file_llseek,
 	.read		= do_sync_read,
 	.write		= do_sync_write,
-	.aio_read		= generic_file_aio_read,
-	.aio_write		= ext3_file_write,
+	.aio_read	= generic_file_aio_read,
+	.aio_write	= ext3_file_aio_write,
 	.readv		= generic_file_readv,
 	.writev		= generic_file_writev,
 	.ioctl		= ext3_ioctl,
fs/nfs/file.c +16 -16
@@ -35,8 +35,10 @@
 #define NFSDBG_FACILITY		NFSDBG_FILE
 
 static int  nfs_file_mmap(struct file *, struct vm_area_struct *);
-static ssize_t nfs_file_read(struct kiocb *, char *, size_t, loff_t);
-static ssize_t nfs_file_write(struct kiocb *, const char *, size_t, loff_t);
+static ssize_t nfs_file_aio_read(struct kiocb *, const struct iovec *,
+		unsigned long, loff_t);
+static ssize_t nfs_file_aio_write(struct kiocb *, const struct iovec *,
+		unsigned long, loff_t);
 static int  nfs_file_flush(struct file *);
 static int  nfs_fsync(struct file *, struct dentry *dentry, int datasync);
 
@@ -44,8 +46,8 @@
 	.llseek		= remote_llseek,
 	.read		= do_sync_read,
 	.write		= do_sync_write,
-	.aio_read		= nfs_file_read,
-	.aio_write		= nfs_file_write,
+	.aio_read	= nfs_file_aio_read,
+	.aio_write	= nfs_file_aio_write,
 	.mmap		= nfs_file_mmap,
 	.open		= nfs_open,
 	.flush		= nfs_file_flush,
@@ -91,19 +93,20 @@
 }
 
 static ssize_t
-nfs_file_read(struct kiocb *iocb, char * buf, size_t count, loff_t pos)
+nfs_file_aio_read(struct kiocb *iocb, const struct iovec *iov,
+		unsigned long nr_segs, loff_t pos)
 {
 	struct dentry * dentry = iocb->ki_filp->f_dentry;
 	struct inode * inode = dentry->d_inode;
 	ssize_t result;
 
-	dfprintk(VFS, "nfs: read(%s/%s, %lu@%lu)\n",
+	dfprintk(VFS, "nfs: read(%s/%s, %lu)\n",
 		dentry->d_parent->d_name.name, dentry->d_name.name,
-		(unsigned long) count, (unsigned long) pos);
+		(unsigned long)pos);
 
 	result = nfs_revalidate_inode(NFS_SERVER(inode), inode);
 	if (!result)
-		result = generic_file_aio_read(iocb, buf, count, pos);
+		result = generic_file_aio_read(iocb, iov, nr_segs, pos);
 	return result;
 }
 
@@ -211,15 +214,16 @@
  * Write to a file (through the page cache).
  */
 static ssize_t
-nfs_file_write(struct kiocb *iocb, const char *buf, size_t count, loff_t pos)
+nfs_file_aio_write(struct kiocb *iocb, const struct iovec *iov,
+		   unsigned long nr_segs, loff_t pos)
 {
 	struct dentry * dentry = iocb->ki_filp->f_dentry;
 	struct inode * inode = dentry->d_inode;
 	ssize_t result;
 
-	dfprintk(VFS, "nfs: write(%s/%s(%ld), %lu@%lu)\n",
+	dfprintk(VFS, "nfs: write(%s/%s(%ld), %lu)\n",
 		dentry->d_parent->d_name.name, dentry->d_name.name,
-		inode->i_ino, (unsigned long) count, (unsigned long) pos);
+		inode->i_ino, (unsigned long) pos);
 
 	result = -EBUSY;
 	if (IS_SWAPFILE(inode))
@@ -228,11 +232,7 @@
 	if (result)
 		goto out;
 
-	result = count;
-	if (!count)
-		goto out;
-
-	result = generic_file_aio_write(iocb, buf, count, pos);
+	result = generic_file_aio_write(iocb, iov, nr_segs, pos);
 out:
 	return result;
 
include/linux/aio.h +6 -0
@@ -2,6 +2,7 @@
 #define __LINUX__AIO_H
 
 #include <linux/list.h>
+#include <linux/uio.h>
 #include <linux/workqueue.h>
 #include <linux/aio_abi.h>
 
@@ -60,6 +61,7 @@
 						 * for cancellation */
 
 	void			*ki_user_obj;	/* pointer to userland's iocb */
+	struct iovec		ki_single;	/* iovec for non-vectored I/O */
 	__u64			ki_user_data;	/* user's data for completion */
 	loff_t			ki_pos;
 
@@ -145,6 +147,10 @@
 extern void FASTCALL(kick_iocb(struct kiocb *iocb));
 extern int FASTCALL(aio_complete(struct kiocb *iocb, long res, long res2));
 extern void FASTCALL(__put_ioctx(struct kioctx *ctx));
+extern ssize_t aio_read_single(struct kiocb *iocb, char *buf,
+			size_t count, loff_t pos);
+extern ssize_t aio_write_single(struct kiocb *iocb, const char *buf,
+			size_t count, loff_t pos);
 struct mm_struct;
 extern void FASTCALL(exit_aio(struct mm_struct *mm));
 
include/linux/fs.h +8 -4
@@ -744,9 +744,11 @@
 	struct module *owner;
 	loff_t (*llseek) (struct file *, loff_t, int);
 	ssize_t (*read) (struct file *, char *, size_t, loff_t *);
-	ssize_t (*aio_read) (struct kiocb *, char *, size_t, loff_t);
+	ssize_t (*aio_read) (struct kiocb *, const struct iovec *,
+			unsigned long, loff_t);
 	ssize_t (*write) (struct file *, const char *, size_t, loff_t *);
-	ssize_t (*aio_write) (struct kiocb *, const char *, size_t, loff_t);
+	ssize_t (*aio_write) (struct kiocb *, const struct iovec *,
+			unsigned long, loff_t);
 	int (*readdir) (struct file *, void *, filldir_t);
 	unsigned int (*poll) (struct file *, struct poll_table_struct *);
 	int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
@@ -1242,8 +1244,10 @@
 extern int file_read_actor(read_descriptor_t * desc, struct page *page, unsigned long offset, unsigned long size);
 extern ssize_t generic_file_read(struct file *, char *, size_t, loff_t *);
 extern ssize_t generic_file_write(struct file *, const char *, size_t, loff_t *);
-extern ssize_t generic_file_aio_read(struct kiocb *, char *, size_t, loff_t);
-extern ssize_t generic_file_aio_write(struct kiocb *, const char *, size_t, loff_t);
+extern ssize_t generic_file_aio_read(struct kiocb *, const struct iovec *,
+				unsigned long, loff_t);
+extern ssize_t generic_file_aio_write(struct kiocb *, const struct iovec *,
+				unsigned long, loff_t);
 extern ssize_t do_sync_read(struct file *filp, char *buf, size_t len, loff_t *ppos);
 extern ssize_t do_sync_write(struct file *filp, const char *buf, size_t len, loff_t *ppos);
 ssize_t generic_file_write_nolock(struct file *file, const struct iovec *iov,
include/net/sock.h +0 -1
@@ -303,7 +303,6 @@
 	struct socket		*sock;
 	struct sock		*sk;
 	struct msghdr		*msg, async_msg;
-	struct iovec		async_iov;
 	struct scm_cookie	*scm, async_scm;
 };
 
mm/filemap.c +6 -7
@@ -884,12 +884,11 @@
 }
 
 ssize_t
-generic_file_aio_read(struct kiocb *iocb, char *buf, size_t count, loff_t pos)
+generic_file_aio_read(struct kiocb *iocb, const struct iovec *iov,
+		unsigned long nr_segs, loff_t pos)
 {
-	struct iovec local_iov = { .iov_base = buf, .iov_len = count };
-
 	BUG_ON(iocb->ki_pos != pos);
-	return __generic_file_aio_read(iocb, &local_iov, 1, &iocb->ki_pos);
+	return __generic_file_aio_read(iocb, iov, nr_segs, &iocb->ki_pos);
 }
 
 ssize_t
@@ -1645,10 +1644,10 @@
 	return err;
 }
 
-ssize_t generic_file_aio_write(struct kiocb *iocb, const char *buf,
-			       size_t count, loff_t pos)
+ssize_t generic_file_aio_write(struct kiocb *iocb, const struct iovec *iov,
+		unsigned long nr_segs, loff_t pos)
 {
-	return generic_file_write(iocb->ki_filp, buf, count, &iocb->ki_pos);
+	return generic_file_writev(iocb->ki_filp, iov, nr_segs, &iocb->ki_pos);
 }
 
 ssize_t generic_file_write(struct file *file, const char *buf,
net/socket.c +22 -22

95 lines not on this page · raw message

#1 lore raw
Quoted 7 lines · Christoph Hellwig

Proposed next steps: Convert over all readv/writev users to aio_read/aio_write and remove the methods. Implement aio_read/aio_write in all filesystems using the generic pagecache code and kill the "normal" generic_file_read and generic_file_write.

Comments?

Please not right now? ;-) At least introduce it as aio_readv/aio_writev as currently the way we deal with iovecs uglifies a lot of code, with no benefit for the vast majority of applications. As for killing generic_file_read/write and using the aio counterparts, that is the plan pending a bit more testing of things. I want to get there step by step without breaking everything along the way.

-ben

#2 lore raw
Quoted 10 lines · Christoph Hellwig
Quoted 7 lines

Proposed next steps: Convert over all readv/writev users to aio_read/aio_write and remove the methods. Implement aio_read/aio_write in all filesystems using the generic pagecache code and kill the "normal" generic_file_read and generic_file_write.

Comments?

Quoted 8 lines

Please not right now? ;-) At least introduce it as aio_readv/aio_writev as currently the way we deal with iovecs uglifies a lot of code, with no benefit for the vast majority of applications. As for killing generic_file_read/write and using the aio counterparts, that is the plan pending a bit more testing of things. I want to get there step by step without breaking everything along the way.

-ben

Here's a patch for aio readv/writev support. Basically it adds:

- two new opcodes (IOCB_CMD_PREADV and IOCB_CMD_PWRITEV)
- a field to the iocb for the user vector
- aio_readv/writev methods to the file_operations structure
- routine aio.c/io_readv_writev, which borrows heavily from do_readv_writev.

I tested this using the aio dio patch that Badari submitted a while back. I compared:
readv/writev io_submit for a vector of N iovecs vs read/write io_submit for N iocbs.

My performance data is only preliminary at this point, but aio readv/writev appears to outperform aio read/write -- twice as fast in some cases. The results generally make sense to me: while there is only one io_submit in both cases, aio readv/writev shortens codepath (one instead of N calls to the underlying filesystem routine) and should normally result in fewer bios/callbacks (at least for direct-io). As importantly, aio readv/writev in my testing also reduces the number of (system) calls to io_getevents.

-Janet

 fs/aio.c                |  110 ++++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/aio_abi.h |   12 +++++
 include/linux/fs.h      |    2 
 3 files changed, 124 insertions(+)
include/linux/aio_abi.h +12 -0
@@ -41,6 +41,8 @@
 	 * IOCB_CMD_POLL = 5,
 	 */
 	IOCB_CMD_NOOP = 6,
+	IOCB_CMD_PREADV = 7, 
+	IOCB_CMD_PWRITEV = 8,
 };
 
 /* read() from /dev/aio returns these structures. */
@@ -65,6 +67,12 @@
  * proper padding and aio_error abstraction
  */
 
+struct io_iocb_vector {
+       	const struct iovec	*vec;           
+       	__s32			nr;
+       	__s64			offset;
+}; 
+
 struct iocb {
 	/* these are internal to the kernel/libc. */
 	__u64	aio_data;	/* data to be returned in event's data */
@@ -80,6 +88,10 @@
 	__u64	aio_nbytes;
 	__s64	aio_offset;
 
+	union {
+		struct io_iocb_vector	v;
+	} u;
+
 	/* extra parameters */
 	__u64	aio_reserved2;	/* TODO: use this for a (struct sigevent *) */
 	__u64	aio_reserved3;
include/linux/fs.h +2 -0
@@ -763,6 +763,8 @@
 	ssize_t (*sendfile) (struct file *, struct file *, loff_t *, size_t);
 	ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
 	unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
+	ssize_t (*aio_readv) (struct kiocb *, const struct iovec *, unsigned long , loff_t *);
+	ssize_t (*aio_writev) (struct kiocb *, const struct iovec *, unsigned long, loff_t *);
 };
 
 struct inode_operations {
fs/aio.c +110 -0

128 lines not on this page · raw message

#3 lore raw
Quoted 5 lines · Janet Morgan

Here's a patch for aio readv/writev support. Basically it adds:

- two new opcodes (IOCB_CMD_PREADV and IOCB_CMD_PWRITEV)
- a field to the iocb for the user vector
- aio_readv/writev methods to the file_operations structure

I presume f_op->aio_readv could point to __generic_file_aio_read for most filesystems.

Would f_op->aio_writev need a new wrapper function for 2.5.42 ? f_op->aio_write eventually calls generic_file_write which uses a different inode from generic_file_writev. So f_op->aio_writev might need to point to a function like generic_file_writev but using the same inode as generic_file_write.

Quoted 7 lines

- routine aio.c/io_readv_writev, which borrows heavily from do_readv_writev.

I tested this using the aio dio patch that Badari submitted a while back. I compared:
readv/writev io_submit for a vector of N iovecs
vs read/write io_submit for N iocbs.

My performance data is only preliminary at this point, but aio readv/writev appears to outperform aio read/write -- twice as fast in some cases. The results generally make sense to me: while there is only one io_submit in both cases, aio readv/writev shortens codepath (one instead of N calls to the underlying filesystem routine) and should normally result in fewer

Twice as fast looks good !

Quoted 1 line

bios/callbacks (at least for direct-io). As importantly, aio readv/writev in my testing also reduces the number of (system) calls to io_getevents.

It would be interesting to see the performance boost when <iov length> events are retrieved at once, using the min_nr parameter of io_getevents.

--Shailabh

#4 lore raw
In reply to Janet Morgan
Quoted 4 lines · Janet Morgan

- two new opcodes (IOCB_CMD_PREADV and IOCB_CMD_PWRITEV)
- a field to the iocb for the user vector
- aio_readv/writev methods to the file_operations structure
- routine aio.c/io_readv_writev, which borrows heavily from do_readv_writev.

A few comments about the interface to userland: don't add fields to the iocb, that breaks the abi. Also, the iovec should be pointed to by iocb->aio_buf, with aio_nbytes containing the length of the iovec (that avoids the need for an additional field). The structure of the iovec itself should probably match the iovec struct, but that means we'll need different opcodes for the 32 bit and 64 bit variants. Alternatively a new iovec that is the same on both (like the iocb) could be defined, but that would be inconvenient for userland.

Within the kernel, the loff_t *pos shouldn't be a pointer -- I'm trying to get rid of extraneous pointers as that means an additional chunk of memory has to be held over the entirety of the aio request. Similarly, the iovec passed into the operation itself can never be allocated on the stack. This probably works for direct io where the iovec gets translated into a scatter gather list of pages, but wouldn't work for something like networking where the iovec itself gets used to determine where in the user address space to copy data (as this can occur after the submit operation has returned).

Aside from those details, I guess if people really need vectored ios it's okay.

-ben

#5 lore raw
Quoted 25 lines · Janet Morgan
Quoted 4 lines

- two new opcodes (IOCB_CMD_PREADV and IOCB_CMD_PWRITEV)
- a field to the iocb for the user vector
- aio_readv/writev methods to the file_operations structure
- routine aio.c/io_readv_writev, which borrows heavily from do_readv_writev.

A few comments about the interface to userland: don't add fields to the iocb, that breaks the abi. Also, the iovec should be pointed to by iocb->aio_buf, with aio_nbytes containing the length of the iovec (that avoids the need for an additional field). The structure of the iovec itself should probably match the iovec struct, but that means we'll need different opcodes for the 32 bit and 64 bit variants. Alternatively a new iovec that is the same on both (like the iocb) could be defined, but that would be inconvenient for userland.

Within the kernel, the loff_t *pos shouldn't be a pointer -- I'm trying to get rid of extraneous pointers as that means an additional chunk of memory has to be held over the entirety of the aio request. Similarly, the iovec passed into the operation itself can never be allocated on the stack. This probably works for direct io where the iovec gets translated into a scatter gather list of pages, but wouldn't work for something like networking where the iovec itself gets used to determine where in the user address space to copy data (as this can occur after the submit operation has returned).

Quoted 5 lines

Aside from those details, I guess if people really need vectored ios it's okay.

-ben

Thanks for the really helpful comments. I've attached another version of the patch.

In addition to (hopefully) addressing the issues you raised, I also removed the calls to locks_verify_area and dnotify_parent from my original patch. Please let me know if you think I should re-instate those calls or if you see any new problems.

-Janet

 fs/aio.c                |  156 ++++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/aio.h     |    1 
 include/linux/aio_abi.h |   14 ++++
 include/linux/fs.h      |    2 
 4 files changed, 173 insertions(+)
include/linux/aio_abi.h +14 -0
@@ -41,6 +41,10 @@
 	 * IOCB_CMD_POLL = 5,
 	 */
 	IOCB_CMD_NOOP = 6,
+	IOCB_CMD_PREADV32 = 7, 
+	IOCB_CMD_PREADV64 = 8, 
+	IOCB_CMD_PWRITEV32 = 9,
+	IOCB_CMD_PWRITEV64 = 10,
 };
 
 /* read() from /dev/aio returns these structures. */
@@ -85,6 +89,16 @@
 	__u64	aio_reserved3;
 }; /* 64 bytes */
 
+struct iovec32 {
+        __u32 iov_base;
+        __u32 iov_len;
+};
+
+struct iovec64 {
+        __u64 iov_base;
+        __u64 iov_len;
+};
+
 #undef IFBIG
 #undef IFLITTLE
 
include/linux/fs.h +2 -0
@@ -764,6 +764,8 @@
 	ssize_t (*sendfile) (struct file *, struct file *, loff_t *, size_t);
 	ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
 	unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
+	ssize_t (*aio_readv) (struct kiocb *, const struct iovec *, unsigned long , loff_t);
+	ssize_t (*aio_writev) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
 };
 
 struct inode_operations {
include/linux/aio.h +1 -0
@@ -51,6 +51,7 @@
 	int			ki_users;
 	unsigned		ki_key;		/* id of this request */
 
+ 	struct iovec		*ki_iov;        /* io vector for readv/writev */
 	struct file		*ki_filp;
 	struct kioctx		*ki_ctx;	/* may be NULL for sync ops */
 	int			(*ki_cancel)(struct kiocb *, struct io_event *);
fs/aio.c +156 -0

186 lines not on this page · raw message