Re: [PATCH] cleanup blockdev_direct_IO locking

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Christoph Hellwig wrote:
> Currently the locking in blockdev_direct_IO is a mess, we have three different
> locking types and very confusing checks for some of them.  The most
> complicated one is DIO_OWN_LOCKING for reads, which happens to not actually be
> used.
> 
> This patch gets rid of the DIO_OWN_LOCKING - as mentioned above the read case
> is unused anyway, and the write side is almost identical to DIO_NO_LOCKING.
> The difference is that DIO_NO_LOCKING always sets the create argument for
> the get_blocks callback to zero, but we can easily move that to the actual
> get_blocks callbacks.  There are four users of the DIO_NO_LOCKING mode:
> gfs already ignores the create argument and thus is fine with the new
> version, ocfs2 only errors out if create were ever set, and we can remove
> this dead code now, the block device code only ever uses create for an
> error message if we are fully beyond the device which can never happen,
> and last but not least XFS will need the new behavour for writes.
> 
> Once this replace the lock_type variable with a flags one, where no flag
> means the DIO_NO_LOCKING behaviour and DIO_LOCKING is kept as the first
> and so far only flag.  Also revamp the documentation of the locking scheme
> to actually make sense.
> 
> Signed-off-by: Christoph Hellwig <hch@xxxxxx>

I generally like it, though I'd rather DIO_LOCKING didn't imply
hole-filling ability; I guess it's related but is it 1:1 ?  Maybe we can
have a hole-filling-related flag right off the bat.  Couple other
comments below inline.

<ocfs2 & xfs snipped>

> Index: linux-2.6/fs/direct-io.c
> ===================================================================
> --- linux-2.6.orig/fs/direct-io.c	2009-08-03 21:34:07.246782220 +0200
> +++ linux-2.6/fs/direct-io.c	2009-08-03 21:34:51.785784373 +0200
> @@ -53,13 +53,6 @@
>   *
>   * If blkfactor is zero then the user's request was aligned to the filesystem's
>   * blocksize.
> - *
> - * lock_type is DIO_LOCKING for regular files on direct-IO-naive filesystems.
> - * This determines whether we need to do the fancy locking which prevents
> - * direct-IO from being able to read uninitialised disk blocks.  If its zero
> - * (blockdev) this locking is not done, and if it is DIO_OWN_LOCKING i_mutex is
> - * not held for the entire direct write (taken briefly, initially, during a
> - * direct read though, but its never held for the duration of a direct-IO).
>   */

Yay for consolidating the locking comments.

>  struct dio {
> @@ -68,7 +61,7 @@ struct dio {
>  	struct inode *inode;
>  	int rw;
>  	loff_t i_size;			/* i_size when submitted */
> -	int lock_type;			/* doesn't change */
> +	int flags;			/* doesn't change */
>  	unsigned blkbits;		/* doesn't change */
>  	unsigned blkfactor;		/* When we're using an alignment which
>  					   is finer than the filesystem's soft
> @@ -240,7 +233,8 @@ static int dio_complete(struct dio *dio,
>  	if (dio->end_io && dio->result)
>  		dio->end_io(dio->iocb, offset, transferred,
>  			    dio->map_bh.b_private);
> -	if (dio->lock_type == DIO_LOCKING)
> +
> +	if (dio->flags & DIO_LOCKING)
>  		/* lockdep: non-owner release */
>  		up_read_non_owner(&dio->inode->i_alloc_sem);
>  
> @@ -515,21 +509,22 @@ static int get_more_blocks(struct dio *d
>  		map_bh->b_state = 0;
>  		map_bh->b_size = fs_count << dio->inode->i_blkbits;
>  
> +		/*
> +		 * For writes inside i_size on a DIO_LOCKING filesystem we
> +		 * forbid block creations: only overwrites are permitted.
> +		 * For this case we fall back to buffered writes at a higher
> +		 * level for inside-i_size block-instantiating writes.


maybe:

  +		 * when an unmapped buffer is returned.

A note that this determination is made by the caller when it gets back
an unmapped bh might help dimwits like me next time I read the code ;)

> +		 * For filesystems with their own locking the decision is
> +		 * left to the get_blocks method.
> +		 */
>  		create = dio->rw & WRITE;
> -		if (dio->lock_type == DIO_LOCKING) {
> +		if (dio->flags & DIO_LOCKING) {
>  			if (dio->block_in_file < (i_size_read(dio->inode) >>
>  							dio->blkbits))
>  				create = 0;
> -		} else if (dio->lock_type == DIO_NO_LOCKING) {
> -			create = 0;
>  		}
>  
> -		/*
> -		 * For writes inside i_size we forbid block creations: only
> -		 * overwrites are permitted.  We fall back to buffered writes
> -		 * at a higher level for inside-i_size block-instantiating
> -		 * writes.
> -		 */
>  		ret = (*dio->get_block)(dio->inode, fs_startblk,
>  						map_bh, create);
>  	}
> @@ -1042,7 +1037,7 @@ direct_io_worker(int rw, struct kiocb *i
>  	 * we can let i_mutex go now that its achieved its purpose
>  	 * of protecting us from looking up uninitialized blocks.
>  	 */
> -	if ((rw == READ) && (dio->lock_type == DIO_LOCKING))
> +	if (rw == READ && (dio->flags & DIO_LOCKING))
>  		mutex_unlock(&dio->inode->i_mutex);
>  
>  	/*
> @@ -1086,30 +1081,29 @@ direct_io_worker(int rw, struct kiocb *i
>  
>  /*
>   * This is a library function for use by filesystem drivers.
> - * The locking rules are governed by the dio_lock_type parameter.
>   *
> - * DIO_NO_LOCKING (no locking, for raw block device access)
> - * For writes, i_mutex is not held on entry; it is never taken.
> - *
> - * DIO_LOCKING (simple locking for regular files)
> - * For writes we are called under i_mutex and return with i_mutex held, even
> - * though it is internally dropped.
> - * For reads, i_mutex is not held on entry, but it is taken and dropped before
> - * returning.
> - *
> - * DIO_OWN_LOCKING (filesystem provides synchronisation and handling of
> - *	uninitialised data, allowing parallel direct readers and writers)
> - * For writes we are called without i_mutex, return without it, never touch it.
> - * For reads we are called under i_mutex and return with i_mutex held, even
> - * though it may be internally dropped.
> - *
> - * Additional i_alloc_sem locking requirements described inline below.
> + * The locking rules are governed by the flags parameter:
> + *  - if the flags value contains DIO_LOCKING we use a fancy locking
> + *    scheme for dumb filesystems.
> + *    For writes this functions is called under i_mutex and return with
                         ^^function                            ^^returns
> + *    i_mutex held, even though it is internally dropped.

Is it?  where?  Oh I guess in direct_io_worker.

> + *    For reads, i_mutex is not held on entry, but it is taken and dropped
> + *    before returning.

It's actually dropped by direct_io_worker, FWIW.

On my initial read of this stuff it sounds like this locking is done in
the function following the comments which confused me.

> + *    For reads and writes i_alloc_sem is taken in shared mode and released
> + *    on I/O completion (which may happen asynchronously after returning to
> + *    the caller).
> + *
> + *  - if the flags value does NOT contain DIO_LOCKING we don't use any
> + *    internal locking but rather rely on the filesystem to synchronize
> + *    direct I/O reads/writes versus each other and truncate.
> + *    For reads and writes both i_mutex and i_alloc_sem are not held on
> + *    entry and are never taken.
>   */
>  ssize_t
>  __blockdev_direct_IO(int rw, struct kiocb *iocb, struct inode *inode,
>  	struct block_device *bdev, const struct iovec *iov, loff_t offset, 
>  	unsigned long nr_segs, get_block_t get_block, dio_iodone_t end_io,
> -	int dio_lock_type)
> +	int flags)
>  {
>  	int seg;
>  	size_t size;
> @@ -1120,8 +1114,6 @@ __blockdev_direct_IO(int rw, struct kioc
>  	ssize_t retval = -EINVAL;
>  	loff_t end = offset;
>  	struct dio *dio;
> -	int release_i_mutex = 0;
> -	int acquire_i_mutex = 0;
>  
>  	if (rw & WRITE)
>  		rw = WRITE_ODIRECT;
> @@ -1156,43 +1148,30 @@ __blockdev_direct_IO(int rw, struct kioc
>  	if (!dio)
>  		goto out;
>  
> -	/*
> -	 * For block device access DIO_NO_LOCKING is used,
> -	 *	neither readers nor writers do any locking at all
> -	 * For regular files using DIO_LOCKING,
> -	 *	readers need to grab i_mutex and i_alloc_sem
> -	 *	writers need to grab i_alloc_sem only (i_mutex is already held)
> -	 * For regular files using DIO_OWN_LOCKING,
> -	 *	neither readers nor writers take any locks here
> -	 */
> -	dio->lock_type = dio_lock_type;
> -	if (dio_lock_type != DIO_NO_LOCKING) {
> +	dio->flags = flags;
> +	if (dio->flags & DIO_LOCKING) {
>  		/* watch out for a 0 len io from a tricksy fs */
>  		if (rw == READ && end > offset) {
> -			struct address_space *mapping;
> +			struct address_space *mapping =
> +					iocb->ki_filp->f_mapping;
>  
> -			mapping = iocb->ki_filp->f_mapping;
> -			if (dio_lock_type != DIO_OWN_LOCKING) {
> -				mutex_lock(&inode->i_mutex);
> -				release_i_mutex = 1;
> -			}
> +			/* will be released by direct_io_worker */

ahah there we go

> +			mutex_lock(&inode->i_mutex);
>  
>  			retval = filemap_write_and_wait_range(mapping, offset,
>  							      end - 1);
>  			if (retval) {
> +				mutex_unlock(&inode->i_mutex);
>  				kfree(dio);
>  				goto out;
>  			}
> -
> -			if (dio_lock_type == DIO_OWN_LOCKING) {
> -				mutex_unlock(&inode->i_mutex);
> -				acquire_i_mutex = 1;
> -			}
>  		}
>  
> -		if (dio_lock_type == DIO_LOCKING)
> -			/* lockdep: not the owner will release it */
> -			down_read_non_owner(&inode->i_alloc_sem);
> +		/*
> +		 * Will be released at I/O completion, possible in a
                                                       ^^possibly
> +		 * different thread.
> +		 */
> +		down_read_non_owner(&inode->i_alloc_sem);
>  	}
>  
>  	/*
> @@ -1210,24 +1189,19 @@ __blockdev_direct_IO(int rw, struct kioc
>  	/*
>  	 * In case of error extending write may have instantiated a few
>  	 * blocks outside i_size. Trim these off again for DIO_LOCKING.
> -	 * NOTE: DIO_NO_LOCK/DIO_OWN_LOCK callers have to handle this by
> -	 * it's own meaner.
> +	 *
> +	 * NOTE: filesystems with their own locking have to handle this
> +	 * on their own.
>  	 */
> -	if (unlikely(retval < 0 && (rw & WRITE))) {
> -		loff_t isize = i_size_read(inode);
> -
> -		if (end > isize && dio_lock_type == DIO_LOCKING)
> -			vmtruncate(inode, isize);
> +	if (dio->flags & DIO_LOCKING) {
> +		if (unlikely((rw & WRITE) && retval < 0)) {
> +			loff_t isize = i_size_read(inode);
> +			if (end > isize )
> +				vmtruncate(inode, isize);
> +		}
>  	}
>  
> -	if (rw == READ && dio_lock_type == DIO_LOCKING)
> -		release_i_mutex = 0;
> -
>  out:
> -	if (release_i_mutex)
> -		mutex_unlock(&inode->i_mutex);
> -	else if (acquire_i_mutex)
> -		mutex_lock(&inode->i_mutex);
>  	return retval;
>  }
>  EXPORT_SYMBOL(__blockdev_direct_IO);
> Index: linux-2.6/include/linux/fs.h
> ===================================================================
> --- linux-2.6.orig/include/linux/fs.h	2009-08-03 21:34:54.254532317 +0200
> +++ linux-2.6/include/linux/fs.h	2009-08-03 21:35:15.455534350 +0200
> @@ -2247,8 +2247,6 @@ ssize_t __blockdev_direct_IO(int rw, str
>  
>  enum {
>  	DIO_LOCKING = 1, /* need locking between buffered and direct access */
> -	DIO_NO_LOCKING,  /* bdev; no locking at all between buffered/direct */
> -	DIO_OWN_LOCKING, /* filesystem locks buffered and direct internally */
>  };

If this is meant to be a flag it should probably be

#define DIO_LOCKING	0x1

not an enum - or if it's an enum put it in hex w/ a comment that the
values are for flags.  enum to me sorta implies that it's sequential but
maybe it's just my hangup ;)

>  static inline ssize_t blockdev_direct_IO(int rw, struct kiocb *iocb,
> @@ -2266,16 +2264,7 @@ static inline ssize_t blockdev_direct_IO
>  	dio_iodone_t end_io)
>  {
>  	return __blockdev_direct_IO(rw, iocb, inode, bdev, iov, offset,
> -				nr_segs, get_block, end_io, DIO_NO_LOCKING);
> -}
> -
> -static inline ssize_t blockdev_direct_IO_own_locking(int rw, struct kiocb *iocb,
> -	struct inode *inode, struct block_device *bdev, const struct iovec *iov,
> -	loff_t offset, unsigned long nr_segs, get_block_t get_block,
> -	dio_iodone_t end_io)
> -{
> -	return __blockdev_direct_IO(rw, iocb, inode, bdev, iov, offset,
> -				nr_segs, get_block, end_io, DIO_OWN_LOCKING);
> +				nr_segs, get_block, end_io, 0);
>  }
>  #endif
>  
> --
> To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in
> the body of a message to majordomo@xxxxxxxxxxxxxxx
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 

--
To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Ext4 Filesystem]     [Union Filesystem]     [Filesystem Testing]     [Ceph Users]     [Ecryptfs]     [AutoFS]     [Kernel Newbies]     [Share Photos]     [Security]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux Cachefs]     [Reiser Filesystem]     [Linux RAID]     [Samba]     [Device Mapper]     [CEPH Development]
  Powered by Linux