Linux block layer
 help / color / mirror / Atom feed
From: "Darrick J. Wong" <djwong@kernel.org>
To: Kanchan Joshi <joshi.k@samsung.com>
Cc: brauner@kernel.org, hch@lst.de, dgc@kernel.org, jack@suse.cz,
	cem@kernel.org, axboe@kernel.dk, kbusch@kernel.org,
	ritesh.list@gmail.com, linux-xfs@vger.kernel.org,
	linux-fsdevel@vger.kernel.org, linux-block@vger.kernel.org,
	gost.dev@samsung.com, Anuj Gupta <anuj1072538@gmail.com>,
	Anuj Gupta <anuj20.g@samsung.com>
Subject: Re: [PATCH v4 3/6] xfs: implement write-stream management support
Date: Mon, 20 Jul 2026 20:08:10 -0700	[thread overview]
Message-ID: <20260721030810.GV7380@frogsfrogsfrogs> (raw)
In-Reply-To: <20260717125538.508925-4-joshi.k@samsung.com>

On Fri, Jul 17, 2026 at 06:25:35PM +0530, Kanchan Joshi wrote:
> From: Anuj Gupta <anuj1072538@gmail.com>
> 
> Implement FS_IOC_WRITE_STREAM_{GET_MAX,OPEN,SET,GET} handlers.
> 
> GET_MAX reports the max write stream ids.
> OPEN allocates an fd for the unused write-stream and returns that.
> SET binds that fd to an open file.
> GET reports the stream-id value set on the file.
> 
> To track used write-streams, a per-mount bitmap is kept.
> The stream fd's release handler clears the bit when the last reference
> drops.
> 
> A new i_write_stream field on xfs_inode holds the bound stream id and
> is propagated to the iomap during block mapping.
> 
> Write streams, filestreams, and write-life-time hints are mutually exclusive;
> combining any two of them returns -EINVAL:
>   - GET_MAX reports 0 whenever xfs_inode_is_filestream() is true,
>     covering both mount-wide filestreams and the per-inode chattr
>     flag. Also when the file is on the realtime device.
>   - SET refuses to bind a stream to a file that already has a
>     write-life-time hint (fcntl F_SET_RW_HINT), is filestream, or is
>     on the realtime device.
>   - chattr refuses to set the filestream or realtime flag on a file
>     that already has a write stream set.

These special "files" that represent stream ids could be generic code
instaed of in xfs.  AFAICT the only thing you need from xfs is a pointer
from struct xfs_inode to struct (xfs_)write_stream, right?

> Suggested-by: Christoph Hellwig <hch@lst.de>
> Co-developed-by: Kanchan Joshi <joshi.k@samsung.com>
> Signed-off-by: Anuj Gupta <anuj20.g@samsung.com>
> Signed-off-by: Kanchan Joshi <joshi.k@samsung.com>
> ---
>  fs/xfs/xfs_icache.c |   1 +
>  fs/xfs/xfs_inode.c  | 155 ++++++++++++++++++++++++++++++++++++++++++++
>  fs/xfs/xfs_inode.h  |   8 +++
>  fs/xfs/xfs_ioctl.c  |  69 ++++++++++++++++++++
>  fs/xfs/xfs_iomap.c  |   1 +
>  fs/xfs/xfs_mount.h  |   3 +
>  fs/xfs/xfs_super.c  |  12 ++++
>  7 files changed, 249 insertions(+)
> 
> diff --git a/fs/xfs/xfs_icache.c b/fs/xfs/xfs_icache.c
> index 9d8dd30bd927..7b9dda74122f 100644
> --- a/fs/xfs/xfs_icache.c
> +++ b/fs/xfs/xfs_icache.c
> @@ -129,6 +129,7 @@ xfs_inode_alloc(
>  	spin_lock_init(&ip->i_ioend_lock);
>  	ip->i_next_unlinked = NULLAGINO;
>  	ip->i_prev_unlinked = 0;
> +	ip->i_write_stream = 0;
>  
>  	return ip;
>  }
> diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c
> index 15279d22a894..aafc3ffa6e0a 100644
> --- a/fs/xfs/xfs_inode.c
> +++ b/fs/xfs/xfs_inode.c
> @@ -4,6 +4,7 @@
>   * All Rights Reserved.
>   */
>  #include <linux/iversion.h>
> +#include <linux/anon_inodes.h>
>  
>  #include "xfs_platform.h"
>  #include "xfs_fs.h"
> @@ -47,6 +48,160 @@
>  
>  struct kmem_cache *xfs_inode_cache;
>  
> +int
> +xfs_inode_max_write_streams(
> +	struct xfs_inode	*ip)
> +{
> +	struct block_device	*bdev;
> +	bool			is_filestream, is_realtime;
> +
> +	xfs_ilock(ip, XFS_ILOCK_SHARED);
> +	is_filestream = xfs_inode_is_filestream(ip);
> +	is_realtime = XFS_IS_REALTIME_INODE(ip);
> +	bdev = xfs_inode_buftarg(ip)->bt_bdev;
> +	xfs_iunlock(ip, XFS_ILOCK_SHARED);
> +
> +	if (!bdev || is_filestream || is_realtime)

Would be nice if realtime worked, or someone at least adds a comment
about why it isn't (e.g. "we have something more exciting for rt/zoned
filesystems") etc.

--D

> +		return 0;
> +
> +	return bdev_max_write_streams(bdev);
> +}
> +
> +uint16_t
> +xfs_inode_get_write_stream(
> +	struct xfs_inode	*ip)
> +{
> +	uint16_t	stream_id;
> +
> +	xfs_ilock(ip, XFS_ILOCK_SHARED);
> +	stream_id = ip->i_write_stream;
> +	xfs_iunlock(ip, XFS_ILOCK_SHARED);
> +
> +	return stream_id;
> +}
> +
> +struct xfs_write_stream {
> +	struct xfs_mount	*mp;
> +	uint16_t		stream_id;	/* 1-based */
> +};
> +
> +static int
> +xfs_write_stream_release(
> +	struct inode		*inode,
> +	struct file		*file)
> +{
> +	struct xfs_write_stream	*ws = file->private_data;
> +	struct xfs_mount	*mp = ws->mp;
> +
> +	spin_lock(&mp->m_streams_lock);
> +	clear_bit(ws->stream_id - 1, mp->m_streams_in_use);
> +	spin_unlock(&mp->m_streams_lock);
> +	kfree(ws);
> +	return 0;
> +}
> +
> +static const struct file_operations xfs_write_stream_fops = {
> +	.release	= xfs_write_stream_release,
> +	.llseek		= noop_llseek,
> +};
> +
> +int
> +xfs_inode_write_stream_open(
> +	struct xfs_inode	*ip,
> +	u32			flags,
> +	u32			*stream_idp)
> +{
> +	struct xfs_mount	*mp = ip->i_mount;
> +	struct xfs_write_stream	*ws;
> +	int			max, slot, fd, ret;
> +
> +	if (flags & ~FS_WRITE_STREAM_OPEN_EXACT)
> +		return -EINVAL;
> +
> +	max = xfs_inode_max_write_streams(ip);
> +	if (!max)
> +		return -EOPNOTSUPP;
> +	ASSERT(mp->m_streams_in_use);
> +
> +	ws = kmalloc(sizeof(*ws), GFP_KERNEL);
> +	if (!ws)
> +		return -ENOMEM;
> +
> +	spin_lock(&mp->m_streams_lock);
> +	if (flags & FS_WRITE_STREAM_OPEN_EXACT) {
> +		if (!*stream_idp || *stream_idp > max) {
> +			ret = -EINVAL;
> +			goto out_unlock;
> +		}
> +		slot = *stream_idp - 1;
> +		if (test_bit(slot, mp->m_streams_in_use)) {
> +			ret = -EBUSY;
> +			goto out_unlock;
> +		}
> +	} else {
> +		slot = find_first_zero_bit(mp->m_streams_in_use, max);
> +		if (slot >= max) {
> +			ret = -EBUSY;
> +			goto out_unlock;
> +		}
> +	}
> +	set_bit(slot, mp->m_streams_in_use);
> +	spin_unlock(&mp->m_streams_lock);
> +
> +	ws->mp = mp;
> +	ws->stream_id = slot + 1;	/* convert to 1-based */
> +
> +	fd = anon_inode_getfd("[xfs_write_stream]", &xfs_write_stream_fops, ws,
> +			      O_RDONLY | O_CLOEXEC);
> +	if (fd < 0) {
> +		spin_lock(&mp->m_streams_lock);
> +		clear_bit(slot, mp->m_streams_in_use);
> +		spin_unlock(&mp->m_streams_lock);
> +		kfree(ws);
> +		return fd;
> +	}
> +
> +	*stream_idp = ws->stream_id;
> +	return fd;
> +
> +out_unlock:
> +	spin_unlock(&mp->m_streams_lock);
> +	kfree(ws);
> +	return ret;
> +}
> +
> +int
> +xfs_inode_set_write_stream(
> +	struct xfs_inode	*ip,
> +	int			stream_fd)
> +{
> +	CLASS(fd, f)(stream_fd);
> +	struct xfs_write_stream	*ws;
> +	int			ret = 0;
> +
> +	if (!fd_file(f))
> +		return -EBADF;
> +	if (fd_file(f)->f_op != &xfs_write_stream_fops)
> +		return -EINVAL;
> +
> +	ws = fd_file(f)->private_data;
> +	if (ws->mp != ip->i_mount)
> +		return -EINVAL;
> +
> +	xfs_ilock(ip, XFS_ILOCK_EXCL);
> +
> +	if (XFS_IS_REALTIME_INODE(ip) || xfs_inode_is_filestream(ip) ||
> +	    VFS_I(ip)->i_write_hint != WRITE_LIFE_NOT_SET) {
> +		ret = -EINVAL;
> +		goto out_unlock;
> +	}
> +
> +	ip->i_write_stream = ws->stream_id;
> +out_unlock:
> +	xfs_iunlock(ip, XFS_ILOCK_EXCL);
> +	return ret;
> +}
> +
>  /*
>   * These two are wrapper routines around the xfs_ilock() routine used to
>   * centralize some grungy code.  They are used in places that wish to lock the
> diff --git a/fs/xfs/xfs_inode.h b/fs/xfs/xfs_inode.h
> index 34c1038ebfcd..6abf82ffbf82 100644
> --- a/fs/xfs/xfs_inode.h
> +++ b/fs/xfs/xfs_inode.h
> @@ -37,6 +37,9 @@ typedef struct xfs_inode {
>  	struct xfs_ifork	i_df;		/* data fork */
>  	struct xfs_ifork	i_af;		/* attribute fork */
>  
> +	/* Write stream information */
> +	uint16_t		i_write_stream;
> +
>  	/* Transaction and locking information. */
>  	struct xfs_inode_log_item *i_itemp;	/* logging information */
>  	struct rw_semaphore	i_lock;		/* inode lock */
> @@ -673,4 +676,9 @@ int xfs_icreate_dqalloc(const struct xfs_icreate_args *args,
>  		struct xfs_dquot **udqpp, struct xfs_dquot **gdqpp,
>  		struct xfs_dquot **pdqpp);
>  
> +int xfs_inode_max_write_streams(struct xfs_inode *ip);
> +uint16_t xfs_inode_get_write_stream(struct xfs_inode *ip);
> +int xfs_inode_write_stream_open(struct xfs_inode *ip, u32 flags,
> +		u32 *stream_idp);
> +int xfs_inode_set_write_stream(struct xfs_inode *ip, int stream_fd);
>  #endif	/* __XFS_INODE_H__ */
> diff --git a/fs/xfs/xfs_ioctl.c b/fs/xfs/xfs_ioctl.c
> index 1b53701bebea..8640e6389768 100644
> --- a/fs/xfs/xfs_ioctl.c
> +++ b/fs/xfs/xfs_ioctl.c
> @@ -557,6 +557,12 @@ xfs_ioctl_setattr_xflags(
>  	bool			rtflag = (fa->fsx_xflags & FS_XFLAG_REALTIME);
>  	uint64_t		i_flags2;
>  
> +	if ((fa->fsx_xflags & FS_XFLAG_FILESTREAM) && ip->i_write_stream)
> +		return -EINVAL;
> +
> +	if (rtflag && ip->i_write_stream)
> +		return -EINVAL;
> +
>  	if (rtflag != XFS_IS_REALTIME_INODE(ip)) {
>  		/* Can't change realtime flag if any extents are allocated. */
>  		if (xfs_inode_has_filedata(ip))
> @@ -1200,6 +1206,59 @@ xfs_ioctl_fs_counts(
>  	return 0;
>  }
>  
> +static int
> +xfs_ioc_write_stream_open(
> +	struct file		*filp,
> +	void __user		*arg)
> +{
> +	struct xfs_inode	*ip = XFS_I(file_inode(filp));
> +	struct fs_write_stream_open wso;
> +	int			fd;
> +
> +	if (copy_from_user(&wso, arg, sizeof(wso)))
> +		return -EFAULT;
> +
> +	fd = xfs_inode_write_stream_open(ip, wso.flags, &wso.stream_id);
> +	if (fd < 0)
> +		return fd;
> +
> +	if (copy_to_user(arg, &wso, sizeof(wso)))
> +		return -EFAULT;
> +	return fd;
> +}
> +
> +static int
> +xfs_ioc_write_stream_set(
> +	struct file		*filp,
> +	unsigned long		arg)
> +{
> +	struct xfs_inode	*ip = XFS_I(file_inode(filp));
> +
> +	if (!(filp->f_mode & FMODE_WRITE))
> +		return -EBADF;
> +	return xfs_inode_set_write_stream(ip, (int)arg);
> +}
> +
> +static int
> +xfs_ioc_write_stream_get(
> +	struct xfs_inode	*ip,
> +	void __user		*arg)
> +{
> +	__u32 stream_id = xfs_inode_get_write_stream(ip);
> +
> +	return put_user(stream_id, (__u32 __user *)arg);
> +}
> +
> +static int
> +xfs_ioc_write_stream_get_max(
> +	struct xfs_inode	*ip,
> +	void __user		*arg)
> +{
> +	__u32 nr_streams = xfs_inode_max_write_streams(ip);
> +
> +	return put_user(nr_streams, (__u32 __user *)arg);
> +}
> +
>  /*
>   * These long-unused ioctls were removed from the official ioctl API in 5.17,
>   * but retain these definitions so that we can log warnings about them.
> @@ -1465,6 +1524,16 @@ xfs_file_ioctl(
>  		return xfs_ioc_health_monitor(filp, arg);
>  	case XFS_IOC_VERIFY_MEDIA:
>  		return xfs_ioc_verify_media(filp, arg);
> +	case FS_IOC_WRITE_STREAM_OPEN:
> +		return xfs_ioc_write_stream_open(filp, (void __user *)arg);
> +	case FS_IOC_WRITE_STREAM_SET:
> +		return xfs_ioc_write_stream_set(filp, p);
> +	case FS_IOC_WRITE_STREAM_GET:
> +		return xfs_ioc_write_stream_get(XFS_I(file_inode(filp)),
> +						(void __user *)arg);
> +	case FS_IOC_WRITE_STREAM_GET_MAX:
> +		return xfs_ioc_write_stream_get_max(XFS_I(file_inode(filp)),
> +						    (void __user *)arg);
>  
>  	default:
>  		return -ENOTTY;
> diff --git a/fs/xfs/xfs_iomap.c b/fs/xfs/xfs_iomap.c
> index 225c3de88d03..bf423897d916 100644
> --- a/fs/xfs/xfs_iomap.c
> +++ b/fs/xfs/xfs_iomap.c
> @@ -144,6 +144,7 @@ xfs_bmbt_to_iomap(
>  	}
>  	iomap->offset = XFS_FSB_TO_B(mp, imap->br_startoff);
>  	iomap->length = XFS_FSB_TO_B(mp, imap->br_blockcount);
> +	iomap->write_stream = ip->i_write_stream;
>  	if (mapping_flags & IOMAP_DAX) {
>  		iomap->dax_dev = target->bt_daxdev;
>  	} else {
> diff --git a/fs/xfs/xfs_mount.h b/fs/xfs/xfs_mount.h
> index 66a02d1b9ad7..1376963fb7e8 100644
> --- a/fs/xfs/xfs_mount.h
> +++ b/fs/xfs/xfs_mount.h
> @@ -349,6 +349,9 @@ typedef struct xfs_mount {
>  
>  	/* Index of uuid record in the uuid xarray. */
>  	unsigned int		m_uuid_table_index;
> +
> +	unsigned long		*m_streams_in_use;
> +	spinlock_t		m_streams_lock;
>  } xfs_mount_t;
>  
>  #define M_IGEO(mp)		(&(mp)->m_ino_geo)
> diff --git a/fs/xfs/xfs_super.c b/fs/xfs/xfs_super.c
> index 8531d526fc44..0e2bf6f7b378 100644
> --- a/fs/xfs/xfs_super.c
> +++ b/fs/xfs/xfs_super.c
> @@ -805,6 +805,7 @@ xfs_mount_free(
>  #ifdef DEBUG
>  	kfree(mp->m_errortag);
>  #endif
> +	bitmap_free(mp->m_streams_in_use);
>  	kfree(mp);
>  }
>  
> @@ -1659,6 +1660,7 @@ xfs_fs_fill_super(
>  	struct xfs_mount	*mp = sb->s_fs_info;
>  	struct inode		*root;
>  	int			flags = 0, error;
> +	int			nr_streams;
>  
>  	mp->m_super = sb;
>  
> @@ -1708,6 +1710,15 @@ xfs_fs_fill_super(
>  	if (error)
>  		return error;
>  
> +	nr_streams = bdev_max_write_streams(mp->m_ddev_targp->bt_bdev);
> +	if (nr_streams) {
> +		mp->m_streams_in_use = bitmap_zalloc(nr_streams, GFP_KERNEL);
> +		if (!mp->m_streams_in_use) {
> +			error = -ENOMEM;
> +			goto out_shutdown_devices;
> +		}
> +	}
> +
>  	if (xfs_debugfs) {
>  		mp->m_debugfs = xfs_debugfs_mkdir(mp->m_super->s_id,
>  						  xfs_debugfs);
> @@ -2249,6 +2260,7 @@ xfs_init_fs_context(
>  #endif
>  
>  	spin_lock_init(&mp->m_sb_lock);
> +	spin_lock_init(&mp->m_streams_lock);
>  	for (i = 0; i < XG_TYPE_MAX; i++)
>  		xa_init(&mp->m_groups[i].xa);
>  	mutex_init(&mp->m_growlock);
> -- 
> 2.25.1
> 
> 

  reply	other threads:[~2026-07-21  3:08 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
     [not found] <CGME20260717125624epcas5p3fefc5b8ff274260bf8fb2f1b225b4f9a@epcas5p3.samsung.com>
2026-07-17 12:55 ` [PATCH v4 0/6] xfs write streams Kanchan Joshi
2026-07-17 12:55   ` [PATCH v4 1/6] fs: add write-stream management ioctls Kanchan Joshi
2026-07-21  3:03     ` Darrick J. Wong
2026-07-17 12:55   ` [PATCH v4 2/6] iomap: introduce and propagate write_stream Kanchan Joshi
2026-07-17 12:55   ` [PATCH v4 3/6] xfs: implement write-stream management support Kanchan Joshi
2026-07-21  3:08     ` Darrick J. Wong [this message]
2026-07-17 12:55   ` [PATCH v4 4/6] xfs: generic AG set based steering Kanchan Joshi
2026-07-21  3:20     ` Darrick J. Wong
2026-07-17 12:55   ` [PATCH v4 5/6] xfs: write stream based AG placement Kanchan Joshi
2026-07-17 12:55   ` [PATCH v4 6/6] xfs: introduce software write streams Kanchan Joshi

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260721030810.GV7380@frogsfrogsfrogs \
    --to=djwong@kernel.org \
    --cc=anuj1072538@gmail.com \
    --cc=anuj20.g@samsung.com \
    --cc=axboe@kernel.dk \
    --cc=brauner@kernel.org \
    --cc=cem@kernel.org \
    --cc=dgc@kernel.org \
    --cc=gost.dev@samsung.com \
    --cc=hch@lst.de \
    --cc=jack@suse.cz \
    --cc=joshi.k@samsung.com \
    --cc=kbusch@kernel.org \
    --cc=linux-block@vger.kernel.org \
    --cc=linux-fsdevel@vger.kernel.org \
    --cc=linux-xfs@vger.kernel.org \
    --cc=ritesh.list@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox