diff mbox series

[v5,7/7] xfs: Support setting FMODE_CAN_ATOMIC_WRITE

Message ID 20240817094800.776408-8-john.g.garry@oracle.com (mailing list archive)
State Accepted, archived
Headers show
Series block atomic writes for xfs | expand

Commit Message

John Garry Aug. 17, 2024, 9:48 a.m. UTC
For when an inode is enabled for atomic writes, set FMODE_CAN_ATOMIC_WRITE
flag. Only direct IO is currently supported, so check for that also.

We rely on the block layer to reject atomic writes which exceed the bdev
request_queue limits, so don't bother checking any such thing here.

Signed-off-by: John Garry <john.g.garry@oracle.com>
---
 fs/xfs/xfs_file.c | 14 ++++++++++++++
 1 file changed, 14 insertions(+)

Comments

Darrick J. Wong Aug. 21, 2024, 5:11 p.m. UTC | #1
On Sat, Aug 17, 2024 at 09:48:00AM +0000, John Garry wrote:
> For when an inode is enabled for atomic writes, set FMODE_CAN_ATOMIC_WRITE
> flag. Only direct IO is currently supported, so check for that also.
> 
> We rely on the block layer to reject atomic writes which exceed the bdev
> request_queue limits, so don't bother checking any such thing here.
> 
> Signed-off-by: John Garry <john.g.garry@oracle.com>
> ---
>  fs/xfs/xfs_file.c | 14 ++++++++++++++
>  1 file changed, 14 insertions(+)
> 
> diff --git a/fs/xfs/xfs_file.c b/fs/xfs/xfs_file.c
> index 9b6530a4eb4a..3489d478809e 100644
> --- a/fs/xfs/xfs_file.c
> +++ b/fs/xfs/xfs_file.c
> @@ -1149,6 +1149,18 @@ xfs_file_remap_range(
>  	return remapped > 0 ? remapped : ret;
>  }
>  
> +static bool xfs_file_open_can_atomicwrite(
> +	struct inode		*inode,
> +	struct file		*file)
> +{
> +	struct xfs_inode	*ip = XFS_I(inode);
> +
> +	if (!(file->f_flags & O_DIRECT))
> +		return false;
> +
> +	return xfs_inode_has_atomicwrites(ip);

...and here too.  I do like the shift to having an incore flag that
controls whether you get untorn write support or not.

--D

> +}
> +
>  STATIC int
>  xfs_file_open(
>  	struct inode	*inode,
> @@ -1157,6 +1169,8 @@ xfs_file_open(
>  	if (xfs_is_shutdown(XFS_M(inode->i_sb)))
>  		return -EIO;
>  	file->f_mode |= FMODE_NOWAIT | FMODE_CAN_ODIRECT;
> +	if (xfs_file_open_can_atomicwrite(inode, file))
> +		file->f_mode |= FMODE_CAN_ATOMIC_WRITE;
>  	return generic_file_open(inode, file);
>  }
>  
> -- 
> 2.31.1
> 
>
John Garry Aug. 22, 2024, 6:04 p.m. UTC | #2
On 21/08/2024 18:11, Darrick J. Wong wrote:
> On Sat, Aug 17, 2024 at 09:48:00AM +0000, John Garry wrote:
>> For when an inode is enabled for atomic writes, set FMODE_CAN_ATOMIC_WRITE
>> flag. Only direct IO is currently supported, so check for that also.
>>
>> We rely on the block layer to reject atomic writes which exceed the bdev
>> request_queue limits, so don't bother checking any such thing here.
>>
>> Signed-off-by: John Garry <john.g.garry@oracle.com>
>> ---
>>   fs/xfs/xfs_file.c | 14 ++++++++++++++
>>   1 file changed, 14 insertions(+)
>>
>> diff --git a/fs/xfs/xfs_file.c b/fs/xfs/xfs_file.c
>> index 9b6530a4eb4a..3489d478809e 100644
>> --- a/fs/xfs/xfs_file.c
>> +++ b/fs/xfs/xfs_file.c
>> @@ -1149,6 +1149,18 @@ xfs_file_remap_range(
>>   	return remapped > 0 ? remapped : ret;
>>   }
>>   
>> +static bool xfs_file_open_can_atomicwrite(
>> +	struct inode		*inode,
>> +	struct file		*file)
>> +{
>> +	struct xfs_inode	*ip = XFS_I(inode);
>> +
>> +	if (!(file->f_flags & O_DIRECT))
>> +		return false;
>> +
>> +	return xfs_inode_has_atomicwrites(ip);
> 
> ...and here too.  I do like the shift to having an incore flag that
> controls whether you get untorn write support or not.

Do you mean that add a new member to xfs_inode to record this? If yes, 
it sounds ok, but we need to maintain consistency (of that member) 
whenever anything which can affect it changes, which is always a bit 
painful.

John
Darrick J. Wong Aug. 22, 2024, 8:44 p.m. UTC | #3
On Thu, Aug 22, 2024 at 07:04:02PM +0100, John Garry wrote:
> On 21/08/2024 18:11, Darrick J. Wong wrote:
> > On Sat, Aug 17, 2024 at 09:48:00AM +0000, John Garry wrote:
> > > For when an inode is enabled for atomic writes, set FMODE_CAN_ATOMIC_WRITE
> > > flag. Only direct IO is currently supported, so check for that also.
> > > 
> > > We rely on the block layer to reject atomic writes which exceed the bdev
> > > request_queue limits, so don't bother checking any such thing here.
> > > 
> > > Signed-off-by: John Garry <john.g.garry@oracle.com>
> > > ---
> > >   fs/xfs/xfs_file.c | 14 ++++++++++++++
> > >   1 file changed, 14 insertions(+)
> > > 
> > > diff --git a/fs/xfs/xfs_file.c b/fs/xfs/xfs_file.c
> > > index 9b6530a4eb4a..3489d478809e 100644
> > > --- a/fs/xfs/xfs_file.c
> > > +++ b/fs/xfs/xfs_file.c
> > > @@ -1149,6 +1149,18 @@ xfs_file_remap_range(
> > >   	return remapped > 0 ? remapped : ret;
> > >   }
> > > +static bool xfs_file_open_can_atomicwrite(
> > > +	struct inode		*inode,
> > > +	struct file		*file)
> > > +{
> > > +	struct xfs_inode	*ip = XFS_I(inode);
> > > +
> > > +	if (!(file->f_flags & O_DIRECT))
> > > +		return false;
> > > +
> > > +	return xfs_inode_has_atomicwrites(ip);
> > 
> > ...and here too.  I do like the shift to having an incore flag that
> > controls whether you get untorn write support or not.
> 
> Do you mean that add a new member to xfs_inode to record this? If yes, it
> sounds ok, but we need to maintain consistency (of that member) whenever
> anything which can affect it changes, which is always a bit painful.

I actually meant something more like:

static bool
xfs_file_open_can_atomicwrite(
	struct file		*file,
	struct inode		*inode)
{
	struct xfs_inode	*ip = XFS_I(inode);
	struct xfs_mount	*mp = ip->i_mount;
	struct xfs_buftarg	*target = xfs_inode_buftarg(ip);

	if (!(file->f_flags & O_DIRECT))
		return false;
	if (!xfs_inode_has_atomicwrites(ip))
		return false;
	if (mp->m_dalign && (mp->m_dalign % ip->i_extsize))
		return false;
	if (mp->m_swidth && (mp->m_swidth % ip->i_extsize))
		return false;
	if (mp->m_sb.sb_blocksize < target->bt_bdev_awu_min)
		return false;
	if (xfs_inode_alloc_unitsize(ip) > target->bt_bdev_awu_max)
		return false;
	return true;
}

--D

> John
>
John Garry Aug. 23, 2024, 10:41 a.m. UTC | #4
On 22/08/2024 21:44, Darrick J. Wong wrote:
>> Do you mean that add a new member to xfs_inode to record this? If yes, it
>> sounds ok, but we need to maintain consistency (of that member) whenever
>> anything which can affect it changes, which is always a bit painful.
> I actually meant something more like:
> 
> static bool
> xfs_file_open_can_atomicwrite(
> 	struct file		*file,
> 	struct inode		*inode)
> {
> 	struct xfs_inode	*ip = XFS_I(inode);
> 	struct xfs_mount	*mp = ip->i_mount;
> 	struct xfs_buftarg	*target = xfs_inode_buftarg(ip);
> 
> 	if (!(file->f_flags & O_DIRECT))
> 		return false;
> 	if (!xfs_inode_has_atomicwrites(ip))
> 		return false;
> 	if (mp->m_dalign && (mp->m_dalign % ip->i_extsize))
> 		return false;
> 	if (mp->m_swidth && (mp->m_swidth % ip->i_extsize))
> 		return false;
> 	if (mp->m_sb.sb_blocksize < target->bt_bdev_awu_min)
> 		return false;
> 	if (xfs_inode_alloc_unitsize(ip) > target->bt_bdev_awu_max)
> 		return false;
> 	return true;
> }

ok, but we should probably factor out some duplicated code with helpers, 
like:

bool xfs_validate_atomicwrites_extsize(struct xfs_mount *mp, uint32_t 
extsize)
{
	if (!is_power_of_2(extsize))
		return false;

	/* Required to guarantee data block alignment */
	if (mp->m_sb.sb_agblocks % extsize)
		return false;

	/* Requires stripe unit+width be a multiple of extsize */
	if (mp->m_dalign && (mp->m_dalign % extsize))
		return false;

	if (mp->m_swidth && (mp->m_swidth % extsize))
		return false;

	return true;
}


bool xfs_inode_has_atomicwrites(struct xfs_inode *ip)
{
	struct xfs_mount	*mp = ip->i_mount;
	struct xfs_buftarg	*target = xfs_inode_buftarg(ip);

	if (!(ip->i_diflags2 & XFS_DIFLAG2_ATOMICWRITES))
		return false;
	if (!xfs_validate_atomicwrites_extsize(mp, ip->i_extsize))
		return false;
	if (mp->m_sb.sb_blocksize < target->bt_bdev_awu_min)
		return false;
	if (xfs_inode_alloc_unitsize(ip) > target->bt_bdev_awu_max)
		return false;
	return true;
}


static bool xfs_file_open_can_atomicwrite(
	struct inode		*inode,
	struct file		*file)
{
	struct xfs_inode	*ip = XFS_I(inode);

	if (!(file->f_flags & O_DIRECT))
		return false;
	return xfs_inode_has_atomicwrites(ip);
}

Those helpers can be re-used in xfs_inode_validate_atomicwrites() and 
xfs_ioctl_setattr_atomicwrites().


John
Darrick J. Wong Aug. 23, 2024, 3:52 p.m. UTC | #5
On Fri, Aug 23, 2024 at 11:41:07AM +0100, John Garry wrote:
> On 22/08/2024 21:44, Darrick J. Wong wrote:
> > > Do you mean that add a new member to xfs_inode to record this? If yes, it
> > > sounds ok, but we need to maintain consistency (of that member) whenever
> > > anything which can affect it changes, which is always a bit painful.
> > I actually meant something more like:
> > 
> > static bool
> > xfs_file_open_can_atomicwrite(
> > 	struct file		*file,
> > 	struct inode		*inode)
> > {
> > 	struct xfs_inode	*ip = XFS_I(inode);
> > 	struct xfs_mount	*mp = ip->i_mount;
> > 	struct xfs_buftarg	*target = xfs_inode_buftarg(ip);
> > 
> > 	if (!(file->f_flags & O_DIRECT))
> > 		return false;
> > 	if (!xfs_inode_has_atomicwrites(ip))
> > 		return false;
> > 	if (mp->m_dalign && (mp->m_dalign % ip->i_extsize))
> > 		return false;
> > 	if (mp->m_swidth && (mp->m_swidth % ip->i_extsize))
> > 		return false;
> > 	if (mp->m_sb.sb_blocksize < target->bt_bdev_awu_min)
> > 		return false;
> > 	if (xfs_inode_alloc_unitsize(ip) > target->bt_bdev_awu_max)
> > 		return false;
> > 	return true;
> > }
> 
> ok, but we should probably factor out some duplicated code with helpers,
> like:
> 
> bool xfs_validate_atomicwrites_extsize(struct xfs_mount *mp, uint32_t
> extsize)

xfs_agblock_t extsize, but other than that this looks right to me.

> {
> 	if (!is_power_of_2(extsize))
> 		return false;
> 
> 	/* Required to guarantee data block alignment */
> 	if (mp->m_sb.sb_agblocks % extsize)
> 		return false;
> 
> 	/* Requires stripe unit+width be a multiple of extsize */
> 	if (mp->m_dalign && (mp->m_dalign % extsize))
> 		return false;
> 
> 	if (mp->m_swidth && (mp->m_swidth % extsize))
> 		return false;
> 
> 	return true;
> }
> 
> 
> bool xfs_inode_has_atomicwrites(struct xfs_inode *ip)
> {
> 	struct xfs_mount	*mp = ip->i_mount;
> 	struct xfs_buftarg	*target = xfs_inode_buftarg(ip);
> 
> 	if (!(ip->i_diflags2 & XFS_DIFLAG2_ATOMICWRITES))
> 		return false;
> 	if (!xfs_validate_atomicwrites_extsize(mp, ip->i_extsize))
> 		return false;
> 	if (mp->m_sb.sb_blocksize < target->bt_bdev_awu_min)
> 		return false;
> 	if (xfs_inode_alloc_unitsize(ip) > target->bt_bdev_awu_max)
> 		return false;
> 	return true;
> }
> 
> 
> static bool xfs_file_open_can_atomicwrite(
> 	struct inode		*inode,
> 	struct file		*file)
> {
> 	struct xfs_inode	*ip = XFS_I(inode);
> 
> 	if (!(file->f_flags & O_DIRECT))
> 		return false;
> 	return xfs_inode_has_atomicwrites(ip);
> }
> 
> Those helpers can be re-used in xfs_inode_validate_atomicwrites() and
> xfs_ioctl_setattr_atomicwrites().

Looks good to me.

--D

> 
> John
> 
>
diff mbox series

Patch

diff --git a/fs/xfs/xfs_file.c b/fs/xfs/xfs_file.c
index 9b6530a4eb4a..3489d478809e 100644
--- a/fs/xfs/xfs_file.c
+++ b/fs/xfs/xfs_file.c
@@ -1149,6 +1149,18 @@  xfs_file_remap_range(
 	return remapped > 0 ? remapped : ret;
 }
 
+static bool xfs_file_open_can_atomicwrite(
+	struct inode		*inode,
+	struct file		*file)
+{
+	struct xfs_inode	*ip = XFS_I(inode);
+
+	if (!(file->f_flags & O_DIRECT))
+		return false;
+
+	return xfs_inode_has_atomicwrites(ip);
+}
+
 STATIC int
 xfs_file_open(
 	struct inode	*inode,
@@ -1157,6 +1169,8 @@  xfs_file_open(
 	if (xfs_is_shutdown(XFS_M(inode->i_sb)))
 		return -EIO;
 	file->f_mode |= FMODE_NOWAIT | FMODE_CAN_ODIRECT;
+	if (xfs_file_open_can_atomicwrite(inode, file))
+		file->f_mode |= FMODE_CAN_ATOMIC_WRITE;
 	return generic_file_open(inode, file);
 }