Re: [PATCH v4 01/22] fs: Add generic_atomic_write_valid_size()

Darrick J. Wong
2024-06-20T21:24:02Z

On Thu, Jun 13, 2024 at 08:35:53AM +0100, John Garry wrote:
> On 12/06/2024 22:10, Darrick J. Wong wrote:
> > On Fri, Jun 07, 2024 at 02:38:58PM +0000, John Garry wrote:
> > > Add a generic helper for FSes to validate that an atomic write is
> > > appropriately sized (along with the other checks).
> > >
> > > Signed-off-by: John Garry <john.g.garry@oracle.com>
> > > ---
> > > include/linux/fs.h | 12 ++++++++++++
> > > 1 file changed, 12 insertions(+)
> > >
> > > diff --git a/include/linux/fs.h b/include/linux/fs.h
> > > index 069cbab62700..e13d34f8c24e 100644
> > > --- a/include/linux/fs.h
> > > +++ b/include/linux/fs.h
> > > @@ -3645,4 +3645,16 @@ bool generic_atomic_write_valid(loff_t pos, struct iov_iter *iter)
> > > return true;
> > > }
> > > +static inline
> > > +bool generic_atomic_write_valid_size(loff_t pos, struct iov_iter *iter,
> > > + unsigned int unit_min, unsigned int unit_max)
> > > +{
> > > + size_t len = iov_iter_count(iter);
> > > +
> > > + if (len < unit_min || len > unit_max)
> > > + return false;
> > > +
> > > + return generic_atomic_write_valid(pos, iter);
> > > +}
> >
> > Now that I look back at "fs: Initial atomic write support" I wonder why
> > not pass the iocb and the iov_iter instead of pos and the iov_iter?
>
> The original user of generic_atomic_write_valid() [blkdev_dio_unaligned() or
> blkdev_dio_invalid() with the rename] used these same args, so I just went
> with that.

Don't let the parameter types of static blockdev helpers determine the
VFS API that filesystems need to implement untorn writes.

In the block layer enablement patch, this could easily be:

bool generic_atomic_write_valid(const struct kiocb *iocb,
const struct iov_iter *iter)
{
size_t len = iov_iter_count(iter);

if (!iter_is_ubuf(iter))
return false;

if (!is_power_of_2(len))
return false;

if (!IS_ALIGNED(iocb->ki_pos, len))
return false;

return true;
}

Then this becomes:

bool generic_atomic_write_valid_size(const struct kiocb *iocb,
const struct iov_iter *iter,
unsigned int unit_min,
unsigned int unit_max)
{
size_t len = iov_iter_count(iter);

if (len < unit_min || len > unit_max)
return false;

return generic_atomic_write_valid(iocb, iter);
}

Yes, that means you have to rearrange the calling conventions of
blkdev_dio_invalid a little bit, but the first two arguments match
->read_iter and ->write_iter. Filesystem writers can see that the first
two arguments are the first two parameters to foofs_write_iter() and
focus on the hard part, which is figuring out unit_{min,max}.

static ssize_t
xfs_file_dio_write(
struct kiocb *iocb,
struct iov_iter *from)
{
...
if ((iocb->ki_flags & IOCB_ATOMIC) &&
!generic_atomic_write_valid_size(iocb, from,
i_blocksize(inode),
XFS_FSB_TO_B(mp, ip->i_extsize)))
return -EINVAL;
}

> > And can these be collapsed into a single generic_atomic_write_checks()
> > function?
>
> bdev file operations would then need to use
> generic_atomic_write_valid_size(), and there is no unit_min and unit_max
> size there, apart from bdev awu min and max. And if I checked them, we would
> be duplicating checks (of awu min and max) in the block layer.

Fair enough, I concede this point.

--D

>
> Cheers,
> John
>