From: Nate Diller <hidden> Date: 2007-01-16 01:54:50
This series is an attempt to generalize the async I/O paths to be
implementation agnostic. It completely eliminates knowledge of
the kiocb structure in the generic code and makes it private within the
current aio code. Things get noticeably cleaner without that layering
violation.
The new interface takes a file_endio_t function pointer, and a private data
pointer, which would normally be aio_complete and a kiocb pointer,
respectively. If the aio submission function gets back EIOCBQUEUED, that is
a guarantee that the endio function will be called, or *already has been
called*. If the file_endio_t pointer provided to aio_[read|write] is NULL,
the FS must block on I/O completion, then return either the number of bytes
read, or an error.
I had to touch more areas that I had originally expected, so there are
changes in a corner of the socket code, and a slight behavior change in the
direct-io completion path with affects XFS and OCFS2. I would appreciate
further review there, so I copied some extra people I hope can help.
This patch is against 2.6.20-rc4-mm1. It has been compile-tested at each
stage. It needs some runtime testing yet, but I prefer to get it out for
commentary and test later.
These patches are for RFC only and have not yet been signed off.
NATE
---
Documentation/filesystems/Locking | 11 +
Documentation/filesystems/vfs.txt | 11 +
arch/s390/hypfs/inode.c | 16 +-
drivers/net/pppoe.c | 8 -
drivers/net/tun.c | 13 +-
drivers/usb/gadget/inode.c | 239 +-------------------------------------
fs/aio.c | 74 ++++++-----
fs/bad_inode.c | 10 -
fs/block_dev.c | 109 +++++++++++------
fs/cifs/cifsfs.c | 10 -
fs/compat.c | 56 --------
fs/direct-io.c | 92 ++++++++------
fs/ecryptfs/file.c | 16 +-
fs/ext2/inode.c | 12 -
fs/ext3/file.c | 9 -
fs/ext3/inode.c | 11 -
fs/ext4/file.c | 9 -
fs/ext4/inode.c | 11 -
fs/fat/inode.c | 12 -
fs/fuse/dev.c | 13 +-
fs/gfs2/ops_address.c | 14 +-
fs/hfs/inode.c | 13 --
fs/hfsplus/inode.c | 13 --
fs/jfs/inode.c | 12 -
fs/nfs/direct.c | 92 +++++++-------
fs/nfs/file.c | 62 +++++----
fs/ntfs/file.c | 71 ++---------
fs/ocfs2/aops.c | 24 +--
fs/ocfs2/aops.h | 8 -
fs/ocfs2/file.c | 44 +++---
fs/ocfs2/inode.h | 2
fs/pipe.c | 12 -
fs/read_write.c | 225 ++++++++++++-----------------------
fs/read_write.h | 8 -
fs/reiserfs/inode.c | 13 --
fs/smbfs/file.c | 28 ++--
fs/udf/file.c | 13 +-
fs/xfs/linux-2.6/xfs_aops.c | 44 +++---
fs/xfs/linux-2.6/xfs_file.c | 58 +++++----
fs/xfs/linux-2.6/xfs_lrw.c | 29 ++--
fs/xfs/linux-2.6/xfs_lrw.h | 10 -
fs/xfs/linux-2.6/xfs_vnode.h | 20 +--
include/linux/aio.h | 11 -
include/linux/fs.h | 114 +++++++++---------
include/linux/net.h | 18 +-
include/linux/nfs_fs.h | 12 -
include/net/bluetooth/bluetooth.h | 2
include/net/inet_common.h | 3
include/net/scm.h | 2
include/net/sock.h | 45 +------
include/net/tcp.h | 6
include/net/udp.h | 3
mm/filemap.c | 109 ++++++++---------
net/appletalk/ddp.c | 5
net/atm/common.c | 6
net/atm/common.h | 7 -
net/ax25/af_ax25.c | 7 -
net/bluetooth/af_bluetooth.c | 4
net/bluetooth/hci_sock.c | 7 -
net/bluetooth/l2cap.c | 2
net/bluetooth/rfcomm/sock.c | 8 -
net/bluetooth/sco.c | 3
net/core/sock.c | 12 -
net/dccp/dccp.h | 8 -
net/dccp/probe.c | 3
net/dccp/proto.c | 7 -
net/decnet/af_decnet.c | 7 -
net/econet/af_econet.c | 7 -
net/ipv4/af_inet.c | 5
net/ipv4/raw.c | 8 -
net/ipv4/tcp.c | 7 -
net/ipv4/tcp_probe.c | 3
net/ipv4/udp.c | 9 -
net/ipv4/udp_impl.h | 2
net/ipv6/raw.c | 6
net/ipv6/udp.c | 10 -
net/ipv6/udp_impl.h | 6
net/ipx/af_ipx.c | 7 -
net/irda/af_irda.c | 29 ++--
net/key/af_key.c | 6
net/llc/af_llc.c | 7 -
net/netlink/af_netlink.c | 24 +--
net/netrom/af_netrom.c | 7 -
net/packet/af_packet.c | 11 -
net/rose/af_rose.c | 7 -
net/sctp/socket.c | 9 -
net/socket.c | 199 +++++++++----------------------
net/tipc/socket.c | 28 +---
net/unix/af_unix.c | 116 +++++++-----------
net/wanrouter/af_wanpipe.c | 7 -
net/x25/af_x25.c | 6
sound/core/pcm_native.c | 15 +-
92 files changed, 1009 insertions(+), 1500 deletions(-)
From: Nate Diller <hidden> Date: 2007-01-16 01:54:50
Convert the internals of blkdev_direct_IO to use a generic endio function,
instead of directly calling aio_complete. This may also fix some bugs/races
in this code, for instance it checks bio->bi_size instead of assuming it's
zero, and it atomically accumulates the bytes_done counter (assuming that
the bio completion handler can't race with itself *might* be valid here, but
the direct-io code makes no such assumption). I'm also pretty sure that
the address_space->directIO functions aren't supposed to mess with the
iocb->ki_pos or ->ki_left.
---
diff -urpN -X dontdiff a/fs/block_dev.c b/fs/block_dev.c
@@ -143,16 +165,21 @@ static int blk_end_aio(struct bio *bio, bio_put(bio);}-/* iocb->ki_nbytes stores error code from LLDD */-if(error)-iocb->ki_nbytes=-EIO;--if(atomic_dec_and_test(bio_count))-aio_complete(iocb,iocb->ki_left,iocb->ki_nbytes);+if(error)+io->err=error;+atomic_add(bytes_done,&io->bytes_done);+blk_io_put(io);return0;}+staticvoidblk_io_init(structbdev_aio*io)+{+atomic_set(&io->iocount,1);+atomic_set(&io->bytes_done,0);+io->err=0;+}+#define VEC_SIZE 16structpvec{unsignedshortnr;
@@ -208,24 +235,33 @@ blkdev_direct_IO(int rw, struct kiocb *iunsignedlongaddr;/* user iovec address */size_tcount;/* user iovec len */-size_tnbytes=iocb->ki_nbytes=iocb->ki_left;/* total xfer size */+size_tnbytes;/* total xfer size */loff_tsize;/* size of block device */structbio*bio;-atomic_t*bio_count=&iocb->ki_bio_count;+structbdev_aiostack_io,*io;+file_endio_t*endio=aio_complete;+void*endio_data=iocb;structpage*page;structpvecpvec;pvec.nr=0;pvec.idx=0;+io=&stack_io;+if(endio){+io=kmalloc(sizeof(structbdev_aio),GFP_KERNEL);+if(!io)+return-ENOMEM;+}+blk_io_init(io);+if(pos&blocksize_mask)return-EINVAL;+nbytes=iov_length(iov,nr_segs);size=i_size_read(inode);-if(pos+nbytes>size){+if(pos+nbytes>size)nbytes=size-pos;-iocb->ki_left=nbytes;-}/**checkfirstnon-zeroiovalignment,theremaining
@@ -237,7 +273,6 @@ blkdev_direct_IO(int rw, struct kiocb *iif(addr&blocksize_mask||count&blocksize_mask)return-EINVAL;}while(!count&&++seg<nr_segs);-atomic_set(bio_count,1);while(nbytes){/* roughly estimate number of bio vec needed */
@@ -248,8 +283,8 @@ blkdev_direct_IO(int rw, struct kiocb *i/* bio_alloc should not fail with GFP_KERNEL flag */bio=bio_alloc(GFP_KERNEL,nvec);bio->bi_bdev=I_BDEV(inode);-bio->bi_end_io=blk_end_aio;-bio->bi_private=iocb;+bio->bi_end_io=blk_bio_endio;+bio->bi_private=io;bio->bi_sector=pos>>blkbits;same_bio:cur_off=addr&~PAGE_MASK;
@@ -289,18 +324,27 @@ same_bio:/* bio is ready, submit it */if(rw==READ)bio_set_pages_dirty(bio);-atomic_inc(bio_count);+atomic_inc(&io->iocount);submit_bio(rw,bio);}completion:-iocb->ki_left-=nbytes;-nbytes=iocb->ki_left;-iocb->ki_pos+=nbytes;+if(!endio){+structcompletionevent;++init_completion(&event);+io->endio=NULL;+io->endio_data=&event;++if(!atomic_dec_and_test(&io->iocount))+wait_for_completion(&event);+returnio->err?io->err:atomic_read(&io->bytes_done);+}-if(atomic_dec_and_test(bio_count))-aio_complete(iocb,nbytes,0);+io->endio=endio;+io->endio_data=endio_data;+blk_io_put(io);return-EIOCBQUEUED;backout:
To unsubscribe, send a message with 'unsubscribe linux-aio' in
the body to majordomo@kvack.org. For more info on Linux AIO,
see: http://www.kvack.org/aio/
Don't email: <a href=mailto:"aart@kvack.org">aart@kvack.org</a>
@@ -559,35 +559,32 @@ static int ep_aio_cancel(struct kiocb *ireturnvalue;}-staticssize_tep_aio_read_retry(structkiocb*iocb)+staticintep_aio_read_retry(structkiocb*iocb){structkiocb_priv*priv=iocb->private;-ssize_tlen,total;-inti;+ssize_ttotal;+inti,err=0;/* we "retry" to get the right mm context for this: *//* copy stuff into user buffers */total=priv->actual;-len=0;for(i=0;i<priv->nr_segs;i++){ssize_tthis=min((ssize_t)(priv->iv[i].iov_len),total);if(copy_to_user(priv->iv[i].iov_base,priv->buf,this)){-if(len==0)-len=-EFAULT;+err=-EFAULT;break;}total-=this;-len+=this;if(total==0)break;}kfree(priv->buf);kfree(priv);aio_put_req(iocb);-returnlen;+returnerr;}staticvoidep_aio_complete(structusb_ep*ep,structusb_request*req)
@@ -610,9 +607,7 @@ static void ep_aio_complete(struct usb_eif(unlikely(kiocbIsCancelled(iocb)))aio_put_req(iocb);else-aio_complete(iocb,-req->actual?req->actual:req->status,-req->status);+aio_complete(iocb,req->actual,req->status);}else{/* retry() won't report both; so we hide some faults */if(unlikely(0!=req->status))
@@ -702,8 +702,8 @@ static ssize_t aio_run_iocb(struct kiocb/* Quit retrying if the i/o has been cancelled */if(kiocbIsCancelled(iocb)){-ret=-EINTR;-aio_complete(iocb,ret,0);+err=-EINTR;+aio_complete(iocb,iocb->ki_nbytes-iocb->ki_left,err);/* must not access the iocb after this */gotoout;}
@@ -1341,26 +1346,26 @@ static ssize_t aio_rw_vect_retry(struct /* This means we must have transferred all that we could *//* No need to retry anymore */-if((ret==0)||(iocb->ki_left==0))-ret=iocb->ki_nbytes-iocb->ki_left;+if(iocb->ki_left==0)+ret=0;-returnret;+return(int)ret;}-staticssize_taio_fdsync(structkiocb*iocb)+staticintaio_fdsync(structkiocb*iocb){structfile*file=iocb->ki_filp;-ssize_tret=-EINVAL;+intret=-EINVAL;if(file->f_op->aio_fsync)ret=file->f_op->aio_fsync(iocb,1);returnret;}-staticssize_taio_fsync(structkiocb*iocb)+staticintaio_fsync(structkiocb*iocb){structfile*file=iocb->ki_filp;-ssize_tret=-EINVAL;+intret=-EINVAL;if(file->f_op->aio_fsync)ret=file->f_op->aio_fsync(iocb,0);
@@ -147,12 +147,8 @@ static int blk_end_aio(struct bio *bio, if(error)iocb->ki_nbytes=-EIO;-if(atomic_dec_and_test(bio_count)){-if((long)iocb->ki_nbytes<0)-aio_complete(iocb,iocb->ki_nbytes,0);-else-aio_complete(iocb,iocb->ki_left,0);-}+if(atomic_dec_and_test(bio_count))+aio_complete(iocb,iocb->ki_left,iocb->ki_nbytes);return0;}
@@ -236,15 +234,13 @@ static int dio_complete(struct dio *dio,ret=0;if(dio->result){-transferred=dio->result;-/* Check for short read case */-if((dio->rw==READ)&&((offset+transferred)>dio->i_size))-transferred=dio->i_size-offset;+if((dio->rw==READ)&&((offset+dio->result)>dio->i_size))+dio->result=dio->i_size-offset;}if(dio->end_io&&dio->result)-dio->end_io(dio->iocb,offset,transferred,+dio->end_io(dio->iocb,offset,dio->result,dio->map_bh.b_private);if(dio->lock_type==DIO_LOCKING)/* lockdep: non-owner release */
@@ -254,8 +250,6 @@ static int dio_complete(struct dio *dio,ret=dio->page_errors;if(ret==0)ret=dio->io_error;-if(ret==0)-ret=transferred;returnret;}
@@ -283,8 +277,8 @@ static int dio_bio_end_aio(struct bio *bspin_unlock_irqrestore(&dio->bio_lock,flags);if(remaining==0){-intret=dio_complete(dio,dio->iocb->ki_pos,0);-aio_complete(dio->iocb,ret,0);+interr=dio_complete(dio,dio->iocb->ki_pos,0);+aio_complete(dio->iocb,dio->result,err);kfree(dio);}
@@ -15,10 +15,9 @@structkioctx;/* Notes on cancelling a kiocb:-*Ifakiocbiscancelled,aio_completemayreturn0toindicate-*thatcancelhasnotyetdisposedofthekiocb.Allcancel-*operations*must*callaio_put_reqtodisposeofthekiocb-*toguardagainstraceswiththecompletioncode.+*Ifakiocbiscancelled,aio_completemaynotyethavedisposedof+*thekiocb.Allcanceloperations*must*callaio_put_reqtodispose+*ofthekiocbtoguardagainstraceswiththecompletioncode.*/#define KIOCB_C_CANCELLED 0x01#define KIOCB_C_COMPLETE 0x02
@@ -98,7 +97,7 @@ struct kiocb {structfile*ki_filp;structkioctx*ki_ctx;/* may be NULL for sync ops */int(*ki_cancel)(structkiocb*,structio_event*);-ssize_t(*ki_retry)(structkiocb*);+int(*ki_retry)(structkiocb*);void(*ki_dtor)(structkiocb*);union{
To unsubscribe, send a message with 'unsubscribe linux-aio' in
the body to majordomo@kvack.org. For more info on Linux AIO,
see: http://www.kvack.org/aio/
Don't email: <a href=mailto:"aart@kvack.org">aart@kvack.org</a>
From: Nate Diller <hidden> Date: 2007-01-16 01:54:50
This converts the iternals of nfs's directIO support to use a generic endio
function, instead of directly calling aio_complete. It's pretty easy
because it already has a pretty abstracted completion path.
---
diff -urpN -X dontdiff a/fs/nfs/direct.c b/fs/nfs/direct.c
@@ -369,12 +369,12 @@ extern int nfs3_removexattr (struct dent*/externssize_tnfs_direct_IO(int,structkiocb*,conststructiovec*,loff_t,unsignedlong);-externssize_tnfs_file_direct_read(structkiocb*iocb,+externssize_tnfs_file_direct_read(structfile*file,conststructiovec*iov,unsignedlongnr_segs,-loff_tpos);-externssize_tnfs_file_direct_write(structkiocb*iocb,+loff_t*pos,file_endio_t*endio,void*endio_data);+externssize_tnfs_file_direct_write(structfile*file,conststructiovec*iov,unsignedlongnr_segs,-loff_tpos);+loff_t*pos,file_endio_t*endio,void*endio_data);/**linux/fs/nfs/dir.c--
To unsubscribe, send a message with 'unsubscribe linux-aio' in
the body to majordomo@kvack.org. For more info on Linux AIO,
see: http://www.kvack.org/aio/
Don't email: <a href=mailto:"aart@kvack.org">aart@kvack.org</a>
@@ -632,7 +632,7 @@ static ssize_t sock_aio_read(struct kiocif(pos!=0)return-ESPIPE;-if(iocb->ki_left==0)/* Match SYS5 behaviour */+if(iov_length(iov,nr_segs)==0)/* Match SYS5 behaviour */return0;for(i=0;i<nr_segs;i++)
@@ -660,7 +660,7 @@ static ssize_t sock_aio_write(struct kioif(pos!=0)return-ESPIPE;-if(iocb->ki_left==0)/* Match SYS5 behaviour */+if(iov_length(iov,nr_segs)==0)/* Match SYS5 behaviour */return0;for(i=0;i<nr_segs;i++)--
To unsubscribe, send a message with 'unsubscribe linux-aio' in
the body to majordomo@kvack.org. For more info on Linux AIO,
see: http://www.kvack.org/aio/
Don't email: <a href=mailto:"aart@kvack.org">aart@kvack.org</a>
From: Nate Diller <hidden> Date: 2007-01-16 01:54:50
This converts the internals of __blockdev_direct_IO in fs/direct-io.c to use
a generic endio function, instead of directly calling aio_complete. It also
changes the semantics of dio_iodone to be more friendly to its only users,
xfs and ocfs2. This allows the caller to know how to release locks and tear
down data structures on error.
It also converts the _own_locking and _no_locking variants of
blockdev_direct_IO to use a generic endio function.
---
fs/direct-io.c | 74 ++++++++++++++++++++++++++------------------
fs/gfs2/ops_address.c | 6 +--
fs/ocfs2/aops.c | 15 ++------
fs/ocfs2/aops.h | 8 ----
fs/ocfs2/file.c | 18 ++++------
fs/ocfs2/inode.h | 2 -
fs/xfs/linux-2.6/xfs_aops.c | 33 +++++++------------
include/linux/fs.h | 57 ++++++++++++++++++---------------
8 files changed, 104 insertions(+), 109 deletions(-)
---
diff -urpN -X dontdiff a/fs/direct-io.c b/fs/direct-io.c
@@ -67,7 +67,7 @@ struct dio {structbio*bio;/* bio under assembly */structinode*inode;intrw;-loff_ti_size;/* i_size when submitted */+unsignedmax_to_read;/* (i_size when submitted) - offset */intlock_type;/* doesn't change */unsignedblkbits;/* doesn't change */unsignedblkfactor;/* When we're using an alignment which
@@ -89,6 +89,7 @@ struct dio {intreap_counter;/* rate limit reaping */get_block_t*get_block;/* block mapping function */dio_iodone_t*end_io;/* IO completion function */+void*destructor_data;/* private data for completion fn */sector_tfinal_block_in_bio;/* current final block in bio + 1 */sector_tnext_block_for_io;/* next block to be put under IO,indio_blocksunits*/
@@ -127,7 +128,8 @@ struct dio {structtask_struct*waiter;/* waiting task (NULL if none) *//* AIO related stuff */-structkiocb*iocb;/* kiocb */+file_endio_t*file_endio;/* aio completion function */+void*endio_data;/* private data for aio completion */intis_async;/* is IO async ? */intio_error;/* IO error in completion path */ssize_tresult;/* IO result */
@@ -232,25 +234,21 @@ static int dio_complete(struct dio *dio,*/if(ret==-EIOCBQUEUED)ret=0;+if(ret==0)+ret=dio->page_errors;+if(ret==0)+ret=dio->io_error;if(dio->result){/* Check for short read case */-if((dio->rw==READ)&&((offset+dio->result)>dio->i_size))-dio->result=dio->i_size-offset;+if((dio->rw==READ)&&(dio->result>dio->max_to_read))+dio->result=dio->max_to_read;}-if(dio->end_io&&dio->result)-dio->end_io(dio->iocb,offset,dio->result,-dio->map_bh.b_private);if(dio->lock_type==DIO_LOCKING)/* lockdep: non-owner release */up_read_non_owner(&dio->inode->i_alloc_sem);-if(ret==0)-ret=dio->page_errors;-if(ret==0)-ret=dio->io_error;-returnret;}
@@ -277,8 +275,11 @@ static int dio_bio_end_aio(struct bio *bspin_unlock_irqrestore(&dio->bio_lock,flags);if(remaining==0){-interr=dio_complete(dio,dio->iocb->ki_pos,0);-aio_complete(dio->iocb,dio->result,err);+interr=dio_complete(dio,0);+if(dio->end_io)+dio->end_io(dio->destructor_data,dio->result,+dio->map_bh.b_private);+dio->file_endio(dio->endio_data,dio->result,err);kfree(dio);}
@@ -628,9 +628,9 @@ static ssize_t gfs2_direct_IO(int rw, stif(rv!=1)gotoout;/* dio not valid, fall back to buffered i/o */-rv=blockdev_direct_IO_no_locking(rw,iocb,inode,inode->i_sb->s_bdev,-iov,offset,nr_segs,-gfs2_get_block_direct,NULL);+rv=blockdev_direct_IO_no_locking(rw,file,iov,offset,nr_segs,+gfs2_get_block_direct,NULL,NULL,+aio_complete,iocb);out:gfs2_glock_dq_m(1,&gh);gfs2_holder_uninit(&gh);
@@ -600,16 +600,12 @@ bail:*i_alloc_sem,weusetherw_lockDLMlocktoprotectioononenodefrom*truncationonanother.*/-staticvoidocfs2_dio_end_io(structkiocb*iocb,-loff_toffset,+staticvoidocfs2_dio_end_io(void*destructor_data,ssize_tbytes,void*private){-structinode*inode=iocb->ki_filp->f_path.dentry->d_inode;+structinode*inode=destructor_data;-/* this io's submitter should not have unlocked this before we could */-BUG_ON(!ocfs2_iocb_is_rw_locked(iocb));-ocfs2_iocb_clear_rw_locked(iocb);up_read(&inode->i_alloc_sem);ocfs2_rw_unlock(inode,0);}
@@ -1833,10 +1833,10 @@ static inline void do_generic_file_read(}#ifdef CONFIG_BLOCK-ssize_t__blockdev_direct_IO(intrw,structkiocb*iocb,structinode*inode,-structblock_device*bdev,conststructiovec*iov,loff_toffset,-unsignedlongnr_segs,get_block_tget_block,dio_iodone_tend_io,-intlock_type);+ssize_t__blockdev_direct_IO(intrw,structfile*file,+conststructiovec*iov,loff_toffset,unsignedlongnr_segs,+get_block_tget_block,dio_iodone_tend_io,void*destructor_data,+file_endio_t*endio,void*endio_data,intlock_type);enum{DIO_LOCKING=1,/* need locking between buffered and direct access */
To unsubscribe, send a message with 'unsubscribe linux-aio' in
the body to majordomo@kvack.org. For more info on Linux AIO,
see: http://www.kvack.org/aio/
Don't email: <a href=mailto:"aart@kvack.org">aart@kvack.org</a>
@@ -2888,16 +2888,13 @@ static int reiserfs_releasepage(struct p/* We thank Mingming Cao for helping us understand in great detail whattodointhissectionofthecode.*/-staticssize_treiserfs_direct_IO(intrw,structkiocb*iocb,+staticssize_treiserfs_direct_IO(intrw,structfile*file,conststructiovec*iov,loff_toffset,-unsignedlongnr_segs)+unsignedlongnr_segs,file_endio_t*endio,+void*endio_data){-structfile*file=iocb->ki_filp;-structinode*inode=file->f_mapping->host;--returnblockdev_direct_IO(rw,iocb,inode,inode->i_sb->s_bdev,iov,-offset,nr_segs,-reiserfs_get_blocks_direct_io,NULL);+returnblockdev_direct_IO(rw,file,iov,offset,nr_segs,+reiserfs_get_blocks_direct_io,endio,endio_data);}intreiserfs_setattr(structdentry*dentry,structiattr*attr)
@@ -309,6 +309,8 @@ typedef int (get_block_t)(struct inode *typedefvoid(dio_iodone_t)(void*destructor_data,ssize_tbytes,void*private);+typedefvoid(file_endio_t)(void*endio_data,ssize_tcount,interr);+/**Attributeflags.Theseshouldbeor-edtogethertofigureoutwhat*hasbeenchanged!
@@ -421,8 +423,9 @@ struct address_space_operations {sector_t(*bmap)(structaddress_space*,sector_t);void(*invalidatepage)(structpage*,unsignedlong);int(*releasepage)(structpage*,gfp_t);-ssize_t(*direct_IO)(int,structkiocb*,conststructiovec*iov,-loff_toffset,unsignedlongnr_segs);+ssize_t(*direct_IO)(intrw,structfile*file,conststructiovec*iov,+loff_toffset,unsignedlongnr_segs,+file_endio_t*endio,void*endio_data);structpage*(*get_xip_page)(structaddress_space*,sector_t,int);/* migrate the contents of a page to the specified target */
To unsubscribe, send a message with 'unsubscribe linux-aio' in
the body to majordomo@kvack.org. For more info on Linux AIO,
see: http://www.kvack.org/aio/
Don't email: <a href=mailto:"aart@kvack.org">aart@kvack.org</a>
From: Nate Diller <hidden> Date: 2007-01-16 01:54:50
this patch removes struct sock_iocb
Its purpose seems to have dwindled to a mere container for struct
scm_cookie, and all of the users of scm_cookie seem to require
re-initializing it each time anyway. Besides, keeping such data around from
one call to the next seems to me like a layering violation, if not a bug,
considering that the sync IO code can use this call path too.
All scm_cookie users are converted to unconditionally allocate on the stack,
and sock_iocb and all its helpers are removed. This also simplifies the
socket aio submission path (is that even used?)
---
include/net/scm.h | 2
include/net/sock.h | 26 ---------
net/netlink/af_netlink.c | 18 ++----
net/socket.c | 131 +++++++++++------------------------------------
net/unix/af_unix.c | 77 ++++++++++-----------------
5 files changed, 68 insertions(+), 186 deletions(-)
---
diff -urpN -X dontdiff a/include/net/scm.h b/include/net/scm.h
@@ -1119,9 +1118,7 @@ static int netlink_sendmsg(struct kiocb if(msg->msg_flags&MSG_OOB)return-EOPNOTSUPP;-if(NULL==siocb->scm)-siocb->scm=&scm;-err=scm_send(sock,msg,siocb->scm);+err=scm_send(sock,msg,&scm);if(err<0)returnerr;
@@ -1155,7 +1152,7 @@ static int netlink_sendmsg(struct kiocb NETLINK_CB(skb).dst_group=dst_group;NETLINK_CB(skb).loginuid=audit_get_loginuid(current->audit_context);selinux_get_task_sid(current,&(NETLINK_CB(skb).sid));-memcpy(NETLINK_CREDS(skb),&siocb->scm->creds,sizeof(structucred));+memcpy(NETLINK_CREDS(skb),&scm.creds,sizeof(structucred));/* What can I do? Netlink is asynchronous, so thatwewillhavetosavecurrentcapabilitiesto
@@ -1189,7 +1186,6 @@ static int netlink_recvmsg(struct kiocb structmsghdr*msg,size_tlen,intflags){-structsock_iocb*siocb=kiocb_to_siocb(kiocb);structscm_cookiescm;structsock*sk=sock->sk;structnetlink_sock*nlk=nlk_sk(sk);
@@ -1230,17 +1226,15 @@ static int netlink_recvmsg(struct kiocb if(nlk->flags&NETLINK_RECV_PKTINFO)netlink_cmsg_recv_pktinfo(msg,skb);-if(NULL==siocb->scm){-memset(&scm,0,sizeof(scm));-siocb->scm=&scm;-}-siocb->scm->creds=*NETLINK_CREDS(skb);+memset(&scm,0,sizeof(scm));++scm.creds=*NETLINK_CREDS(skb);skb_free_datagram(sk,skb);if(nlk->cb&&atomic_read(&sk->sk_rmem_alloc)<=sk->sk_rcvbuf/2)netlink_dump(sk);-scm_recv(sock,msg,siocb->scm,flags);+scm_recv(sock,msg,&scm,flags);out:netlink_rcv_wake(sk);
@@ -1559,8 +1550,7 @@ static int unix_dgram_recvmsg(struct kiostructmsghdr*msg,size_tsize,intflags){-structsock_iocb*siocb=kiocb_to_siocb(iocb);-structscm_cookietmp_scm;+structscm_cookiescm;structsock*sk=sock->sk;structunix_sock*u=unix_sk(sk);intnoblock=flags&MSG_DONTWAIT;
@@ -1593,17 +1583,14 @@ static int unix_dgram_recvmsg(struct kioif(err)gotoout_free;-if(!siocb->scm){-siocb->scm=&tmp_scm;-memset(&tmp_scm,0,sizeof(tmp_scm));-}-siocb->scm->creds=*UNIXCREDS(skb);-unix_set_secdata(siocb->scm,skb);+memset(&scm,0,sizeof(scm));+scm.creds=*UNIXCREDS(skb);+unix_set_secdata(&scm,skb);if(!(flags&MSG_PEEK)){if(UNIXCB(skb).fp)-unix_detach_fds(siocb->scm,skb);+unix_detach_fds(&scm,skb);}else{
@@ -1620,11 +1607,11 @@ static int unix_dgram_recvmsg(struct kio*/if(UNIXCB(skb).fp)-siocb->scm->fp=scm_fp_dup(UNIXCB(skb).fp);+scm.fp=scm_fp_dup(UNIXCB(skb).fp);}err=size;-scm_recv(sock,msg,siocb->scm,flags);+scm_recv(sock,msg,&scm,flags);out_free:skb_free_datagram(sk,skb);
@@ -1672,8 +1659,7 @@ static int unix_stream_recvmsg(struct kistructmsghdr*msg,size_tsize,intflags){-structsock_iocb*siocb=kiocb_to_siocb(iocb);-structscm_cookietmp_scm;+structscm_cookiescm;structsock*sk=sock->sk;structunix_sock*u=unix_sk(sk);structsockaddr_un*sunaddr=msg->msg_name;
@@ -1700,10 +1686,7 @@ static int unix_stream_recvmsg(struct ki*whilesleepsinmemcpy_tomsg*/-if(!siocb->scm){-siocb->scm=&tmp_scm;-memset(&tmp_scm,0,sizeof(tmp_scm));-}+memset(&scm,0,sizeof(scm));mutex_lock(&u->readlock);
@@ -1743,13 +1726,13 @@ static int unix_stream_recvmsg(struct kiif(check_creds){/* Never glue messages from different writers */-if(memcmp(UNIXCREDS(skb),&siocb->scm->creds,sizeof(siocb->scm->creds))!=0){+if(memcmp(UNIXCREDS(skb),&scm.creds,sizeof(scm.creds))!=0){skb_queue_head(&sk->sk_receive_queue,skb);break;}}else{/* Copy credentials */-siocb->scm->creds=*UNIXCREDS(skb);+scm.creds=*UNIXCREDS(skb);check_creds=1;}
@@ -1776,7 +1759,7 @@ static int unix_stream_recvmsg(struct kiskb_pull(skb,chunk);if(UNIXCB(skb).fp)-unix_detach_fds(siocb->scm,skb);+unix_detach_fds(&scm,skb);/* put the skb back if we didn't use it up.. */if(skb->len)
@@ -1787,7 +1770,7 @@ static int unix_stream_recvmsg(struct kikfree_skb(skb);-if(siocb->scm->fp)+if(scm.fp)break;}else
@@ -1795,7 +1778,7 @@ static int unix_stream_recvmsg(struct ki/* It is questionable, see note in unix_dgram_recvmsg.*/if(UNIXCB(skb).fp)-siocb->scm->fp=scm_fp_dup(UNIXCB(skb).fp);+scm.fp=scm_fp_dup(UNIXCB(skb).fp);/* put message back and return */skb_queue_head(&sk->sk_receive_queue,skb);
@@ -1804,7 +1787,7 @@ static int unix_stream_recvmsg(struct ki}while(size);mutex_unlock(&u->readlock);-scm_recv(sock,msg,siocb->scm,flags);+scm_recv(sock,msg,&scm,flags);out:returncopied?:err;}
@@ -2108,7 +2108,7 @@ err_out:/* For now, when the user asks for O_SYNC, we actually give O_DSYNC. */if(likely(!status)){if(unlikely((file->f_flags&O_SYNC)||IS_SYNC(vi))){-if(!mapping->a_ops->writepage||!is_sync_kiocb(iocb))+if(!mapping->a_ops->writepage||endio)status=generic_osync_inode(vi,mapping,OSYNC_METADATA|OSYNC_DATA);}
@@ -450,37 +408,41 @@ unsigned long iov_shorten(struct iovec *returnseg;}-ssize_tdo_sync_readv_writev(structfile*filp,conststructiovec*iov,-unsignedlongnr_segs,size_tlen,loff_t*ppos,iov_fn_tfn)+ssize_tdo_loop_readv_writev(inttype,structfile*filp,structiovec*iov,+unsignedlongnr_segs,loff_t*ppos,size_tcount){-structkiocbkiocb;-ssize_tret;+structiovec*vector=iov;+io_fn_tfn=NULL;+ssize_tret=0;-init_sync_kiocb(&kiocb,filp);-kiocb.ki_pos=*ppos;-kiocb.ki_left=len;-kiocb.ki_nbytes=len;--for(;;){-ret=fn(&kiocb,iov,nr_segs,kiocb.ki_pos);-if(ret!=-EIOCBRETRY)-break;-wait_on_retry_sync_kiocb(&kiocb);-}+if(count==0)+gotoout;-if(ret==-EIOCBQUEUED)-ret=wait_on_sync_kiocb(&kiocb);-*ppos=kiocb.ki_pos;-returnret;-}+ret=rw_verify_area(type,filp,ppos,count);+if(ret<0)+gotoout;-/* Do it by hand, with file-ops */-ssize_tdo_loop_readv_writev(structfile*filp,structiovec*iov,-unsignedlongnr_segs,loff_t*ppos,io_fn_tfn)-{-structiovec*vector=iov;-ssize_tret=0;+ret=security_file_permission(filp,type==READ?MAY_READ:MAY_WRITE);+if(ret)+gotoout;++if(type==READ){+if(filp->f_op->aio_read)+ret=filp->f_op->aio_read(filp,iov,nr_segs,ppos,+NULL,NULL);+fn=filp->f_op->read;+}else{+if(filp->f_op->aio_write)+ret=filp->f_op->aio_write(filp,iov,nr_segs,ppos,+NULL,NULL);+fn=filp->f_op->write;+}+if(!fn)+gotoout;+/*+*There'snoaio_*function,sodoeachvectorbyhand+*/while(nr_segs>0){void__user*base;size_tlen;
@@ -502,51 +464,54 @@ ssize_t do_loop_readv_writev(struct fileif(nr!=len)break;}-+out:+if((ret+(type==READ))>0){+if(type==READ)+fsnotify_access(filp->f_path.dentry);+else+fsnotify_modify(filp->f_path.dentry);+}returnret;}/* A write operation does a read from user space and vice versa */#define vrfy_dir(type) ((type) == READ ? VERIFY_WRITE : VERIFY_READ)-ssize_trw_copy_check_uvector(inttype,conststructiovec__user*uvector,-unsignedlongnr_segs,unsignedlongfast_segs,-structiovec*fast_pointer,-structiovec**ret_pointer)-{-unsignedlongseg;-ssize_tret;-structiovec*iov=fast_pointer;+ssize_tiov_check_alloc(unsignedlongnr_segs,unsignedlongfast_segs,+structiovec**ret_ptr)+{+/*+*SuSsays"The readv() function *may* fail if the iovcnt argument+*waslessthanorequalto0,orgreaterthan{IOV_MAX}.Linuxhas+*traditionallyreturnedzeroforzerosegments,so...+*/+if(nr_segs==0)+return0;-/*-*SuSsays"The readv() function *may* fail if the iovcnt argument-*waslessthanorequalto0,orgreaterthan{IOV_MAX}.Linuxhas-*traditionallyreturnedzeroforzerosegments,so...-*/-if(nr_segs==0){-ret=0;-gotoout;+if(nr_segs>UIO_MAXIOV)+return-EINVAL;++if(nr_segs>fast_segs){+*ret_ptr=kmalloc(nr_segs*sizeof(structiovec),GFP_KERNEL);+if(*ret_ptr==NULL)+return-ENOMEM;}+return0;+}+++ssize_trw_copy_check_uvector(inttype,conststructiovec__user*uvector,+unsignedlongnr_segs,structiovec*iov)+{+unsignedlongseg;+ssize_tret=0;/**Firstgetthe"struct iovec"fromusermemoryand*verifyallthepointers*/-if(nr_segs>UIO_MAXIOV){-ret=-EINVAL;-gotoout;-}-if(nr_segs>fast_segs){-iov=kmalloc(nr_segs*sizeof(structiovec),GFP_KERNEL);-if(iov==NULL){-ret=-ENOMEM;-gotoout;-}-}-if(copy_from_user(iov,uvector,nr_segs*sizeof(*uvector))){-ret=-EFAULT;-gotoout;-}+if(copy_from_user(iov,uvector,nr_segs*sizeof(*uvector)))+return-EFAULT;/**AccordingtotheSingleUnixSpecificationweshouldreturnEINVAL
@@ -554,26 +519,20 @@ ssize_t rw_copy_check_uvector(int type, *totallengthwouldoverflowthessize_treturnvalueofthe*systemcall.*/-ret=0;for(seg=0;seg<nr_segs;seg++){void__user*buf=iov[seg].iov_base;ssize_tlen=(ssize_t)iov[seg].iov_len;/* see if we we're about to use an invalid len or if*it'sabouttooverflowssize_t*/-if(len<0||(ret+len<ret)){-ret=-EINVAL;-gotoout;-}-if(unlikely(!access_ok(vrfy_dir(type),buf,len))){-ret=-EFAULT;-gotoout;-}+if(len<0||(ret+len<ret))+return-EINVAL;++if(unlikely(!access_ok(vrfy_dir(type),buf,len)))+return-EFAULT;ret+=len;}-out:-*ret_pointer=iov;returnret;}
From: Nate Diller <hidden> Date: 2007-01-16 01:54:50
This removes the aio implementation from the usb gadget file system. Aside
from making very creative (!) use of the aio retry path, it can't be of any
use performance-wise because it always kmalloc()s a bounce buffer for the
*whole* I/O size. Perhaps the only reason to keep it around is the ability
to cancel I/O requests, which only applies when using the user space async
I/O interface. I highly doubt that is enough incentive to justify the extra
complexity here or in user-space, so I think it's a safe bet to remove this.
If that feature still desired, it would be possible to implement a sync
interface that does an interruptible sleep.
I can be convinced otherwise, but the alternatives are difficult. See for
example the "fuse, get_user_pages, flush_anon_page, aliasing caches and all
that again" LKML thread recently for why it's waaay easier to kmalloc a
bounce buffer here, and (ab)use the retry interface.
---
diff -urpN -X dontdiff a/drivers/usb/gadget/inode.c b/drivers/usb/gadget/inode.c
@@ -527,218 +527,6 @@ static int ep_ioctl (struct inode *inode/*----------------------------------------------------------------------*/-/* ASYNCHRONOUS ENDPOINT I/O OPERATIONS (bulk/intr/iso) */--structkiocb_priv{-structusb_request*req;-structep_data*epdata;-void*buf;-conststructiovec*iv;-unsignedlongnr_segs;-unsignedactual;-};--staticintep_aio_cancel(structkiocb*iocb,structio_event*e)-{-structkiocb_priv*priv=iocb->private;-structep_data*epdata;-intvalue;--local_irq_disable();-epdata=priv->epdata;-// spin_lock(&epdata->dev->lock);-kiocbSetCancelled(iocb);-if(likely(epdata&&epdata->ep&&priv->req))-value=usb_ep_dequeue(epdata->ep,priv->req);-else-value=-EINVAL;-// spin_unlock(&epdata->dev->lock);-local_irq_enable();--aio_put_req(iocb);-returnvalue;-}--staticintep_aio_read_retry(structkiocb*iocb)-{-structkiocb_priv*priv=iocb->private;-ssize_ttotal;-inti,err=0;--/* we "retry" to get the right mm context for this: */--/* copy stuff into user buffers */-total=priv->actual;-for(i=0;i<priv->nr_segs;i++){-ssize_tthis=min((ssize_t)(priv->iv[i].iov_len),total);--if(copy_to_user(priv->iv[i].iov_base,priv->buf,this)){-err=-EFAULT;-break;-}--total-=this;-if(total==0)-break;-}-kfree(priv->buf);-kfree(priv);-aio_put_req(iocb);-returnerr;-}--staticvoidep_aio_complete(structusb_ep*ep,structusb_request*req)-{-structkiocb*iocb=req->context;-structkiocb_priv*priv=iocb->private;-structep_data*epdata=priv->epdata;--/* lock against disconnect (and ideally, cancel) */-spin_lock(&epdata->dev->lock);-priv->req=NULL;-priv->epdata=NULL;-if(priv->iv==NULL-||unlikely(req->actual==0)-||unlikely(kiocbIsCancelled(iocb))){-kfree(req->buf);-kfree(priv);-iocb->private=NULL;-/* aio_complete() reports bytes-transferred _and_ faults */-if(unlikely(kiocbIsCancelled(iocb)))-aio_put_req(iocb);-else-aio_complete(iocb,req->actual,req->status);-}else{-/* retry() won't report both; so we hide some faults */-if(unlikely(0!=req->status))-DBG(epdata->dev,"%s fault %d len %d\n",-ep->name,req->status,req->actual);--priv->buf=req->buf;-priv->actual=req->actual;-kick_iocb(iocb);-}-spin_unlock(&epdata->dev->lock);--usb_ep_free_request(ep,req);-put_ep(epdata);-}--staticssize_t-ep_aio_rwtail(-structkiocb*iocb,-char*buf,-size_tlen,-structep_data*epdata,-conststructiovec*iv,-unsignedlongnr_segs-)-{-structkiocb_priv*priv;-structusb_request*req;-ssize_tvalue;--priv=kmalloc(sizeof*priv,GFP_KERNEL);-if(!priv){-value=-ENOMEM;-fail:-kfree(buf);-returnvalue;-}-iocb->private=priv;-priv->iv=iv;-priv->nr_segs=nr_segs;--value=get_ready_ep(iocb->ki_filp->f_flags,epdata);-if(unlikely(value<0)){-kfree(priv);-gotofail;-}--iocb->ki_cancel=ep_aio_cancel;-get_ep(epdata);-priv->epdata=epdata;-priv->actual=0;--/* each kiocb is coupled to one usb_request, but we can't-*allocateorsubmitthoseifthehostdisconnected.-*/-spin_lock_irq(&epdata->dev->lock);-if(likely(epdata->ep)){-req=usb_ep_alloc_request(epdata->ep,GFP_ATOMIC);-if(likely(req)){-priv->req=req;-req->buf=buf;-req->length=len;-req->complete=ep_aio_complete;-req->context=iocb;-value=usb_ep_queue(epdata->ep,req,GFP_ATOMIC);-if(unlikely(0!=value))-usb_ep_free_request(epdata->ep,req);-}else-value=-EAGAIN;-}else-value=-ENODEV;-spin_unlock_irq(&epdata->dev->lock);--up(&epdata->lock);--if(unlikely(value)){-kfree(priv);-put_ep(epdata);-}else-value=(iv?-EIOCBRETRY:-EIOCBQUEUED);-returnvalue;-}--staticssize_t-ep_aio_read(structkiocb*iocb,conststructiovec*iov,-unsignedlongnr_segs,loff_to)-{-structep_data*epdata=iocb->ki_filp->private_data;-char*buf;-size_tlen=iov_length(iov,nr_segs);--if(unlikely(epdata->desc.bEndpointAddress&USB_DIR_IN))-return-EINVAL;--buf=kmalloc(len,GFP_KERNEL);-if(unlikely(!buf))-return-ENOMEM;--iocb->ki_retry=ep_aio_read_retry;-returnep_aio_rwtail(iocb,buf,len,epdata,iov,nr_segs);-}--staticssize_t-ep_aio_write(structkiocb*iocb,conststructiovec*iov,-unsignedlongnr_segs,loff_to)-{-structep_data*epdata=iocb->ki_filp->private_data;-char*buf;-size_tlen=0;-inti=0;--if(unlikely(!(epdata->desc.bEndpointAddress&USB_DIR_IN)))-return-EINVAL;--buf=kmalloc(iov_length(iov,nr_segs),GFP_KERNEL);-if(unlikely(!buf))-return-ENOMEM;--for(i=0;i<nr_segs;i++){-if(unlikely(copy_from_user(&buf[len],iov[i].iov_base,-iov[i].iov_len)!=0)){-kfree(buf);-return-EFAULT;-}-len+=iov[i].iov_len;-}-returnep_aio_rwtail(iocb,buf,len,epdata,NULL,0);-}--/*----------------------------------------------------------------------*/-/* used after endpoint configuration */staticconststructfile_operationsep_io_operations={.owner=THIS_MODULE,
@@ -119,7 +119,7 @@ int bt_sock_register(int proto, struct intbt_sock_unregister(intproto);voidbt_sock_link(structbt_sock_list*l,structsock*s);voidbt_sock_unlink(structbt_sock_list*l,structsock*s);-intbt_sock_recvmsg(structkiocb*iocb,structsocket*sock,structmsghdr*msg,size_tlen,intflags);+intbt_sock_recvmsg(structsocket*sock,structmsghdr*msg,size_tlen,intflags);uintbt_sock_poll(structfile*file,structsocket*sock,poll_table*wait);intbt_sock_wait_state(structsock*sk,intstate,unsignedlongtimeo);
@@ -618,7 +612,7 @@ static int send_stream(struct kiocb *iocintres;if(likely(total_len<=TIPC_MAX_USER_MSG_SIZE))-returnsend_packet(iocb,sock,m,total_len);+returnsend_packet(sock,m,total_len);/* Can only send large data streams if already connected */
@@ -657,7 +651,7 @@ static int send_stream(struct kiocb *ioc?curr_left:TIPC_MAX_USER_MSG_SIZE;my_iov.iov_base=curr_start;my_iov.iov_len=bytes_to_send;-if((res=send_packet(iocb,sock,&my_msg,0))<0){+if((res=send_packet(sock,&my_msg,0))<0){returnbytes_sent?bytes_sent:res;}curr_left-=bytes_to_send;
@@ -792,7 +786,6 @@ static int anc_data_recv(struct msghdr */** *recv_msg-receivepacket-orientedmessage-*@iocb:(unused)*@m:descriptorformessageinfo*@buf_len:totalsizeofuserbufferarea*@flags:receiveflags
@@ -803,8 +796,8 @@ static int anc_data_recv(struct msghdr **Returnssizeofreturnedmessagedata,errnootherwise*/-staticintrecv_msg(structkiocb*iocb,structsocket*sock,-structmsghdr*m,size_tbuf_len,intflags)+staticintrecv_msg(structsocket*sock,structmsghdr*m,+size_tbuf_len,intflags){structtipc_sock*tsock=tipc_sk(sock->sk);structsk_buff*buf;
To unsubscribe, send a message with 'unsubscribe linux-aio' in
the body to majordomo@kvack.org. For more info on Linux AIO,
see: http://www.kvack.org/aio/
Don't email: <a href=mailto:"aart@kvack.org">aart@kvack.org</a>
From: Christoph Hellwig <hch@infradead.org> Date: 2007-01-16 02:14:38
On Mon, Jan 15, 2007 at 05:54:50PM -0800, Nate Diller wrote:
Convert code using iocb->ki_left to use the more generic iov_length() call.
No way. We need to reduce the numer of iovec traversals, not adding
more of them.
--
To unsubscribe, send a message with 'unsubscribe linux-aio' in
the body to majordomo@kvack.org. For more info on Linux AIO,
see: http://www.kvack.org/aio/
Don't email: <a href=mailto:"aart@kvack.org">aart@kvack.org</a>
From: Christoph Hellwig <hch@infradead.org> Date: 2007-01-16 03:24:12
On Mon, Jan 15, 2007 at 05:54:50PM -0800, Nate Diller wrote:
This series is an attempt to generalize the async I/O paths to be
implementation agnostic. It completely eliminates knowledge of
the kiocb structure in the generic code and makes it private within the
current aio code. Things get noticeably cleaner without that layering
violation.
The new interface takes a file_endio_t function pointer, and a private data
pointer, which would normally be aio_complete and a kiocb pointer,
respectively. If the aio submission function gets back EIOCBQUEUED, that is
a guarantee that the endio function will be called, or *already has been
called*. If the file_endio_t pointer provided to aio_[read|write] is NULL,
the FS must block on I/O completion, then return either the number of bytes
read, or an error.
I don't really like this patchet at all. At some point it's a lot nicer
to have a lot of paramaters that are related and passed down a long
callchain into a structure, and I think the aio code is over that threshold.
The completion function cleanups look okay to me, but I'd rather add
that completion function to struct kiocb instead of removing kiocb use.
I have this slight feeling you want to use this completions for something
else than the current aio code, if that's the case it would help
if you could explain briefly in what direction your heading.
From: Nate Diller <hidden> Date: 2007-01-16 04:25:18
On 1/15/07, Christoph Hellwig [off-list ref] wrote:
On Mon, Jan 15, 2007 at 05:54:50PM -0800, Nate Diller wrote:
quoted
This series is an attempt to generalize the async I/O paths to be
implementation agnostic. It completely eliminates knowledge of
the kiocb structure in the generic code and makes it private within the
current aio code. Things get noticeably cleaner without that layering
violation.
The new interface takes a file_endio_t function pointer, and a private data
pointer, which would normally be aio_complete and a kiocb pointer,
respectively. If the aio submission function gets back EIOCBQUEUED, that is
a guarantee that the endio function will be called, or *already has been
called*. If the file_endio_t pointer provided to aio_[read|write] is NULL,
the FS must block on I/O completion, then return either the number of bytes
read, or an error.
I don't really like this patchet at all. At some point it's a lot nicer
to have a lot of paramaters that are related and passed down a long
callchain into a structure, and I think the aio code is over that threshold.
The completion function cleanups look okay to me, but I'd rather add
that completion function to struct kiocb instead of removing kiocb use.
I have this slight feeling you want to use this completions for something
else than the current aio code, if that's the case it would help
if you could explain briefly in what direction your heading.
Actually I agree with you more than you might think. I had intended
this to mesh with your struct iodesc idea, where iodesc would contain
the iovec pointer, nr_segs, iov_length, and whatever else needs to be
there, potentially even the endio function and its private data, tying
those to the iovec instead of a separate structure that needs to be
kept in sync. There's a distinct layering that should exist between
things that should accompany the iovec transparently, and private data
that should be attached opaquely by layers above.
The biggest thing I have in mind for this patch, actually, is to fix
up the *sync* paths. I don't think we should be waiting on sync I/O
at the *top* of the call stack, like with wait_on_sync_kiocb(), I'd
say the best place to wait is at the *bottom*, down in the I/O
scheduler. This would make it a lot easier to clean up the completion
paths, because in the sync case, you'd be right back in process
context again as you traverse upward through the RAID, encryption,
loopback, directIO, FS log commit, etc. It doesn't by itself
eliminate the need for all the threads and workqueues and such that
those layers each own, but it is a step in the right direction.
Now if you want to talk about long-term vaporware style ideas, yeah, I
do have my own thoughts on how aio should work. And from Agami's
perspective, this patch also makes it easier for us to do certain
debugging traces that we wish to hack together, in order to profile
performance on our platform. But I'd be hesitant to make those
arguments, cause they are largely irrelevant (we can obviously carry
the patch for debugging without buy-in from the community). This is
the right thing to do from a design perspective. Hopefully it enables
a new architecture that can reduce context switches in I/O completion,
and reduce overhead. That's the real motive ;)
NATE
From: Nate Diller <hidden> Date: 2007-01-16 05:37:31
On 1/15/07, Christoph Hellwig [off-list ref] wrote:
On Mon, Jan 15, 2007 at 05:54:50PM -0800, Nate Diller wrote:
quoted
Convert code using iocb->ki_left to use the more generic iov_length() call.
No way. We need to reduce the numer of iovec traversals, not adding
more of them.
ok, I can work on a version of this that uses struct iodesc. Maybe
something like this?
struct iodesc {
struct iovec *iov;
unsigned long nr_segs;
size_t nbytes;
};
I suppose it's worth doing the iodesc thing along with this patchset
anyway, since it'll avoid an extra round of interface churn.
NATE
From: Stephen Hemminger <hidden> Date: 2007-01-16 05:46:14
On Mon, 15 Jan 2007 17:54:50 -0800
Nate Diller [off-list ref] wrote:
Remove unused arg from socket operations
The sendmsg and recvmsg socket operations take a kiocb pointer, but none of
the functions actually use it. There's really no need even theoretically,
it's really quite ugly having it there at all. Also, removing it will pave
the way for a more generic completion path in the file_operations.
---
Would getting rid of these make later implementation of AIO networking
harder?
@@ -559,35 +559,32 @@ static int ep_aio_cancel(struct kiocb *ireturnvalue;}-staticssize_tep_aio_read_retry(structkiocb*iocb)+staticintep_aio_read_retry(structkiocb*iocb){structkiocb_priv*priv=iocb->private;-ssize_tlen,total;-inti;+ssize_ttotal;+inti,err=0;/* we "retry" to get the right mm context for this: *//* copy stuff into user buffers */total=priv->actual;-len=0;for(i=0;i<priv->nr_segs;i++){ssize_tthis=min((ssize_t)(priv->iv[i].iov_len),total);if(copy_to_user(priv->iv[i].iov_base,priv->buf,this)){-if(len==0)-len=-EFAULT;+err=-EFAULT;
Discarding the capability to report partial success, e.g. that the first N
bytes were properly transferred? I don't see any virtue in that change.
Quite the opposite in fact.
I think you're also expecting that if N bytes were requested, that's always
how many will be received. That's not true for packetized I/O such as USB
isochronous transfers ... where it's quite legit (and in some cases routine)
for the other end to send packets that are shorter than the maximum allowed.
Sending a zero length packet is not the same as sending no packet at all,
for another example.
From: David Brownell <hidden> Date: 2007-01-16 06:05:43
On Monday 15 January 2007 5:54 pm, Nate Diller wrote:
This removes the aio implementation from the usb gadget file system.
NAK. I see a deep mis-understanding here.
Aside
from making very creative (!) use of the aio retry path, it can't be of any
use performance-wise
Other than the basic win of letting one userspace thread keep an I/O
stream active while at the same time processing the data it reads or
writes?? That's the "async" part of AIO.
There's a not-so-little thing called "I/O overlap" ... which is the only
way to prevent wasting bandwidth between (non-cacheable) I/O requests,
and thus is the only way to let userspace code achieve anything close
to the maximum I/O bandwidth the hardware can achieve.
We want to see the host side "usbfs" evolve to support AIO like this
too, for the same reasons. (Currently it has fairly ugly AIO code
that looks unlike any other AIO code in Linux. Recent updates to
support a file-per-endpoint device model are a necessary precursor
to switching over to standard AIO syscalls.)
because it always kmalloc()s a bounce buffer for the
*whole* I/O size.
By and large that's a negligible factor compared to being able to
achieve I/O overlap. ISTR the reason for not doing fancy DMA magic
was that the cost of this style AIO was under 1 KByte object code
on ARM, which was easy to justify ... while DMA magic to do that
sort of stuff would be much fatter, as well as more error prone.
(And that's why the "creative" use of the retry path. As I've
observed before, "retry" is a misnomer in the general sense of
an async I/O framework. It's more of a semi-completion callback;
I/O can't in general be "retried" on error or fault, and even in
the current usage it's not really a "retry".)
Now that high speed peripheral hardware is becoming more common on
embedded Linuxes -- TI has DaVinci, OMAP 2430, TUSB6010 (as found
in the new Nokia 800 tablets); Atmel AVR32 AP7000; at least a couple
parts that should be able to use the same musb_hdrc driver as those
TI parts; and a few other chips I've heard of -- there may be some
virtue in eliminating the memcpy, since those CPUs don't have many
MIPS to waste. (Iff the memcpy turns out to be a real issue...)
Perhaps the only reason to keep it around is the ability
to cancel I/O requests, which only applies when using the user space async
I/O interface.
It's good to have almost the complete kernel API functionality
exposed to userspace, and having I/O cancelation is an inevitable
consequence of a complete AIO framework ... but that particular
issue was not a driving concern.
The reason for AIO is to have a *STANDARD* userspace interface
for *ASYNC I/O* which otherwise can't exist. You know, the kind
of I/O interface that can't be implemented with read() and write()
syscalls, which for non-buffered I/O necessarily preclude all I/O
overlap. AIO itself is a direct match to most I/O frameworks'
primitives. (AIOCB being directly analagous to peripheral side
"struct usb_request" and host side "struct urb".)
You know, I've always thought that one reason the AIO discussions
seemed strange is that they weren't really focussed on I/O (the
lowlevel after-the-caches stuff) so much as filesystems (several
layers up in the stack, with intervening caching frameworks).
The first several implementations of AIO that I saw were restricted
to "real" I/O and not applicable to disk backed files. So while I
was glad the Linux approach didn't make that mistake, it's seemed
that it might be wanting to make a converse mistake: neglecting I/O
that isn't aimed at data stored on disks.
I highly doubt that is enough incentive to justify the extra
complexity here or in user-space, so I think it's a safe bet to remove this.
If that feature still desired, it would be possible to implement a sync
interface that does an interruptible sleep.
What's needed is an async, non-sleeeping, interface ... with I/O
overlap. That's antithetical to using read()/write() calls, so
your proposed approach couldn't possibly work.
- Dave
From: David Brownell <hidden> Date: 2007-01-16 08:22:41
On Monday 15 January 2007 8:25 pm, Nate Diller wrote:
I don't think we should be waiting on sync I/O
at the *top* of the call stack, like with wait_on_sync_kiocb(), I'd
say the best place to wait is at the *bottom*, down in the I/O
scheduler.
Erm ... *what* I/O scheduler? These I/O requests may go directly
to the end of the hardware I/O queue, which already has an I/O model
where each request can correspond directly to a KIOCB. And which
does not include any synchronous primitives.
No such scheduler has previously been, or _should_ be, required.
From: Nate Diller <hidden> Date: 2007-01-16 09:13:48
On 1/15/07, David Brownell [off-list ref] wrote:
On Monday 15 January 2007 5:54 pm, Nate Diller wrote:
quoted
This removes the aio implementation from the usb gadget file system.
NAK. I see a deep mis-understanding here.
quoted
Aside
from making very creative (!) use of the aio retry path, it can't be of any
use performance-wise
Other than the basic win of letting one userspace thread keep an I/O
stream active while at the same time processing the data it reads or
writes?? That's the "async" part of AIO.
There's a not-so-little thing called "I/O overlap" ... which is the only
way to prevent wasting bandwidth between (non-cacheable) I/O requests,
and thus is the only way to let userspace code achieve anything close
to the maximum I/O bandwidth the hardware can achieve.
We want to see the host side "usbfs" evolve to support AIO like this
too, for the same reasons. (Currently it has fairly ugly AIO code
that looks unlike any other AIO code in Linux. Recent updates to
support a file-per-endpoint device model are a necessary precursor
to switching over to standard AIO syscalls.)
quoted
because it always kmalloc()s a bounce buffer for the
*whole* I/O size.
By and large that's a negligible factor compared to being able to
achieve I/O overlap. ISTR the reason for not doing fancy DMA magic
was that the cost of this style AIO was under 1 KByte object code
on ARM, which was easy to justify ... while DMA magic to do that
sort of stuff would be much fatter, as well as more error prone.
(And that's why the "creative" use of the retry path. As I've
observed before, "retry" is a misnomer in the general sense of
an async I/O framework. It's more of a semi-completion callback;
I/O can't in general be "retried" on error or fault, and even in
the current usage it's not really a "retry".)
Now that high speed peripheral hardware is becoming more common on
embedded Linuxes -- TI has DaVinci, OMAP 2430, TUSB6010 (as found
in the new Nokia 800 tablets); Atmel AVR32 AP7000; at least a couple
parts that should be able to use the same musb_hdrc driver as those
TI parts; and a few other chips I've heard of -- there may be some
virtue in eliminating the memcpy, since those CPUs don't have many
MIPS to waste. (Iff the memcpy turns out to be a real issue...)
quoted
Perhaps the only reason to keep it around is the ability
to cancel I/O requests, which only applies when using the user space async
I/O interface.
It's good to have almost the complete kernel API functionality
exposed to userspace, and having I/O cancelation is an inevitable
consequence of a complete AIO framework ... but that particular
issue was not a driving concern.
The reason for AIO is to have a *STANDARD* userspace interface
for *ASYNC I/O* which otherwise can't exist. You know, the kind
of I/O interface that can't be implemented with read() and write()
syscalls, which for non-buffered I/O necessarily preclude all I/O
overlap. AIO itself is a direct match to most I/O frameworks'
primitives. (AIOCB being directly analagous to peripheral side
"struct usb_request" and host side "struct urb".)
You know, I've always thought that one reason the AIO discussions
seemed strange is that they weren't really focussed on I/O (the
lowlevel after-the-caches stuff) so much as filesystems (several
layers up in the stack, with intervening caching frameworks).
The first several implementations of AIO that I saw were restricted
to "real" I/O and not applicable to disk backed files. So while I
was glad the Linux approach didn't make that mistake, it's seemed
that it might be wanting to make a converse mistake: neglecting I/O
that isn't aimed at data stored on disks.
quoted
I highly doubt that is enough incentive to justify the extra
complexity here or in user-space, so I think it's a safe bet to remove this.
If that feature still desired, it would be possible to implement a sync
interface that does an interruptible sleep.
What's needed is an async, non-sleeeping, interface ... with I/O
overlap. That's antithetical to using read()/write() calls, so
your proposed approach couldn't possibly work.
haha, wow ok you convinced me :)
I got a bit impatient when I was working on this, it took some time
just to figure out the intention of the code, and I'm trying to hold
to a bit of a schedule here. Without any clear (to me) reason, I
didn't want to spend a lot of effort fixing this up.
There's really no big difference between the usb drivers here and the
disk I/O scheduler queue, AFAICT, so it seems like the solution I want
is to do a kmap() on the user buffer and then do the I/O straight out
of that. That will eliminate the need for the bounce buffer. I'll
post a new version along with the iodesc changes later this week.
NATE
@@ -559,35 +559,32 @@ static int ep_aio_cancel(struct kiocb *ireturnvalue;}-staticssize_tep_aio_read_retry(structkiocb*iocb)+staticintep_aio_read_retry(structkiocb*iocb){structkiocb_priv*priv=iocb->private;-ssize_tlen,total;-inti;+ssize_ttotal;+inti,err=0;/* we "retry" to get the right mm context for this: *//* copy stuff into user buffers */total=priv->actual;-len=0;for(i=0;i<priv->nr_segs;i++){ssize_tthis=min((ssize_t)(priv->iv[i].iov_len),total);if(copy_to_user(priv->iv[i].iov_base,priv->buf,this)){-if(len==0)-len=-EFAULT;+err=-EFAULT;
Discarding the capability to report partial success, e.g. that the first N
bytes were properly transferred? I don't see any virtue in that change.
Quite the opposite in fact.
I think you're also expecting that if N bytes were requested, that's always
how many will be received. That's not true for packetized I/O such as USB
isochronous transfers ... where it's quite legit (and in some cases routine)
for the other end to send packets that are shorter than the maximum allowed.
Sending a zero length packet is not the same as sending no packet at all,
for another example.
I will convert this (usb) code to use the standard completion path,
which you will notice *gained* the ability to properly report both an
error and a partial success as part of this patch. In fact, fixing
this up was my intention when I wrote this patch, and the later patch
was a compromise intended to get this whole bundle out for review in a
timely manner :)
NATE
On Mon, Jan 15, 2007 at 09:44:27PM -0800, Stephen Hemminger (shemminger@osdl.org) wrote:
quoted
The sendmsg and recvmsg socket operations take a kiocb pointer, but none of
the functions actually use it. There's really no need even theoretically,
it's really quite ugly having it there at all. Also, removing it will pave
the way for a more generic completion path in the file_operations.
---
Would getting rid of these make later implementation of AIO networking
harder?
Depending on what AIO it will be.
Mainstream AIO does stand on kiocb, but if socket operations will be
extended to have additional async_read/write (like it as done in kevent
AIO) there is no need to have this pointer in sync operations (until
people want to have sync aio just as async with waiting for completion).
So, real question is, what next - how network AIO will be implemented?
--
Evgeniy Polyakov
From: David Brownell <hidden> Date: 2007-01-16 18:36:49
On Tuesday 16 January 2007 1:13 am, Nate Diller wrote:
On 1/15/07, David Brownell [off-list ref] wrote:
quoted
What's needed is an async, non-sleeeping, interface ... with I/O
overlap. That's antithetical to using read()/write() calls, so
your proposed approach couldn't possibly work.
haha, wow ok you convinced me :)
Good. :)
I got a bit impatient when I was working on this, it took some time
just to figure out the intention of the code, and I'm trying to hold
to a bit of a schedule here. Without any clear (to me) reason, I
didn't want to spend a lot of effort fixing this up.
Thing is, it's not OK to break other subsystems like that.
There's really no big difference between the usb drivers here and the
disk I/O scheduler queue, AFAICT,
The disk scheduler queue is more complex, as I understand things,
since it can combine operations. For USB, "combining" would break
essential semantics relied on by both sides of the transaction.
Maybe the best way to view this is to accept that with USB, all
scheduler operations (e.g. for bandwidth reservation management)
are out of scope of the AIO request model. AIO requests are no
more (or less) than "append this to the endpoint's I/O queue",
with the (host side) I/O scheduling handled separately.
so it seems like the solution I want
is to do a kmap() on the user buffer and then do the I/O straight out
of that. That will eliminate the need for the bounce buffer. I'll
post a new version along with the iodesc changes later this week.
Sounds more complex, but it would be nice to have that code become
zero-copy instead of single-copy. That'd let some platforms work
with high bandwidth ISO from userspace, which previously would not
have had enough CPU bandwidth. ("High bandwidth" means sustained
8-24 MByte/sec data streaming. Processing pixels at that rate may
require a companion DSP...) Testing will be different issue though.
- Dave
On Tuesday, 16. January 2007 06:37, Nate Diller wrote:
On 1/15/07, Christoph Hellwig [off-list ref] wrote:
quoted
On Mon, Jan 15, 2007 at 05:54:50PM -0800, Nate Diller wrote:
quoted
Convert code using iocb->ki_left to use the more generic iov_length() call.
No way. We need to reduce the numer of iovec traversals, not adding
more of them.
ok, I can work on a version of this that uses struct iodesc. Maybe
something like this?
struct iodesc {
struct iovec *iov;
unsigned long nr_segs;
size_t nbytes;
};
I suppose it's worth doing the iodesc thing along with this patchset
anyway, since it'll avoid an extra round of interface churn.
What about this instead
struct iodesc {
struct iovec *iov;
unsigned long nr_segs;
unsigned long seg_limit;
size_t nr_bytes;
};
That will enable resizeable iodescs with partial completion state and
will enable successive filling of an iodesc with iovs.
This will be needed anyway. I built an complete short userspace
module for that already. I can post and GPLv2 it somewhere, if people
are interested.
Regards
Ingo Oeser
From: Benjamin LaHaise <bcrl@kvack.org> Date: 2007-01-17 21:52:30
On Mon, Jan 15, 2007 at 08:25:15PM -0800, Nate Diller wrote:
the right thing to do from a design perspective. Hopefully it enables
a new architecture that can reduce context switches in I/O completion,
and reduce overhead. That's the real motive ;)
And it's a broken motive. Context switches per se are not bad, as they
make it possible to properly schedule code in a busy system (which is
*very* important when realtime concerns come into play). Have a look
at how things were done in the 2.4 aio code to see how completion would
get done with a non-retry method, typically in interrupt context. I had
code that did direct I/O rather differently by sharing code with the
read/write code paths at some point, the catch being that it was pretty
invasive, which meant that it never got merged with the changes to handle
writeback pressure and other work that happened during 2.5.
That said, you can't make kiocb private without completely removing the
ability of the rest of the kernel to complete an aio sanely from irq context.
You need some form of i/o descriptor, and a kiocb is just that. Adding more
layering is just going to make things messier and slower for no real gain.
-ben
--
"Time is of no importance, Mr. President, only life is important."
Don't Email: [off-list ref].
--
To unsubscribe, send a message with 'unsubscribe linux-aio' in
the body to majordomo@kvack.org. For more info on Linux AIO,
see: http://www.kvack.org/aio/
Don't email: <a href=mailto:"aart@kvack.org">aart@kvack.org</a>
From: Nate Diller <hidden> Date: 2007-01-17 23:32:45
On Wed, 17 Jan 2007, Benjamin LaHaise wrote:
On Mon, Jan 15, 2007 at 08:25:15PM -0800, Nate Diller wrote:
quoted
the right thing to do from a design perspective. Hopefully it enables
a new architecture that can reduce context switches in I/O completion,
and reduce overhead. That's the real motive ;)
And it's a broken motive. Context switches per se are not bad, as they
make it possible to properly schedule code in a busy system (which is
*very* important when realtime concerns come into play). Have a look
at how things were done in the 2.4 aio code to see how completion would
get done with a non-retry method, typically in interrupt context. I had
code that did direct I/O rather differently by sharing code with the
read/write code paths at some point, the catch being that it was pretty
invasive, which meant that it never got merged with the changes to handle
writeback pressure and other work that happened during 2.5.
I'm having some trouble understanding your concern. From my perspective,
any unnecessary context switch represents not only performance loss, but
extra complexity in the code. In this case, I'm not suggesting that the
aio.c code causes problems, quite the opposite. The code I'd like to change
is FS and md levels, where context switches happen because of timers,
workqueues, and worker threads. For sync I/O, these layers could be doing
their completion work in process context, but because waiting on sync I/O is
done in layers above, they must resort to other means, even for the common
case. The dm-crypt module is the most straightforward example.
I took a look at some 2.4.18 aio patches in kernel.org/.../bcrl/aio/, and if
I understand what you did, you were basically operating at the aops level
rather than f_ops. I actually like that idea, it's nicer than having the
direct-io code do its work seperately from the aio code. Part of where I'm
going with this patch is a better integration between the block layer
(make_request), page layer (aops), and FS layer (f_ops), particularly in the
completion paths. The direct-io code is an improvement over the common code
on that point, do_readahead() and friends all wait on individual pages to
become uptodate. I'd like to bring some improvements from the directIO
architecture into use in the common case, which I hope will help
performance.
I know that might seem somewhat unrelated, but I don't think it is. This
change goes hand in hand with using completion handlers in the aops. That
will link together the completion callback in the bio with the aio callback,
so that the whole stack can finish its work in one context.
That said, you can't make kiocb private without completely removing the
ability of the rest of the kernel to complete an aio sanely from irq context.
You need some form of i/o descriptor, and a kiocb is just that. Adding more
layering is just going to make things messier and slower for no real gain.
This patchset does not change how or when I/O completion happens,
aio_complete() will still get called from direct-io.c, nfs-direct.c, et al.
The iocb structure is still passed to aio_complete, just like before. The
only difference is that the lower level code doesn't know that it's got an
iocb, all it sees is an opaque cookie. It's more like enforcing a layer
that's already in place, and I think things got simpler rather than messier.
Whether things are slower or not remains to be seen, but I expect no
measurable changes either way with this patch.
I'm releasing a new version of the patch soon, it will use a new iodesc
structure to keep track of iovec state, which simplifies things further. It
also will have a new version of the usb gadget code, and some general
cleanups. I hope you'll take a look at it.
NATE