* [PATCH v3 0/6] xfs write streams
[not found] <CGME20260616181240epcas5p3f86fbb67f0d04cb0ee4b34839c9522b5@epcas5p3.samsung.com>
@ 2026-06-16 18:05 ` Kanchan Joshi
2026-06-16 18:05 ` [PATCH v3 1/6] fs: add generic write-stream management ioctl Kanchan Joshi
` (5 more replies)
0 siblings, 6 replies; 10+ messages in thread
From: Kanchan Joshi @ 2026-06-16 18:05 UTC (permalink / raw)
To: brauner, hch, djwong, dgc, jack, cem, axboe, kbusch, ritesh.list
Cc: linux-xfs, linux-fsdevel, linux-block, gost.dev, Kanchan Joshi
Hi All,
In LSFMMBPF'26, we discussed 'write stream' as a mechanism to reduce the
filesystem allocator bottlenecks and improving buffered/direct IO
scalability, in different sessions.
This series introduces a generic interface [1,2] for write stream management on
files. It achieves spatial isolation and concurrency improvments [3] in xfs using
- generic AG-set (patch #4)
- write-stream based AG-set (patch #5)
write streams allow the abstraction provider (fs, block device, raid etc.) to
leverage application's intent (file relationships/lifecycle).
- application: sends grouping/isolation intent with a stream id.
- xfs: maps streams to AGs; allocates without interleaving; gains higher
concurrency due to reduced lock contention.
- hardware: maps streams to underlying allocation unit; reduces device
internal write amplification, improved life, predictable QoS.
Also few other points:
- Since high level write streams (in xfs) can work without the
low-level write streams (in block device), the series has a general
value beyond hardware with a particular capability.
- For hardware-only spatial isolation, only first 3 patches are needed.
- write-stream is different from existing 'filestream' allocator which
maintains directory-to-AG associations in a global MRU cache. That
requires state managment and memory (and its reclaim). Proposed AG-set
based steering relies on simple, statless/lockless airthmatic that aligns
more with the default allocator heuristics.
[3]
### Performance
1. On regular NVMe:
fio 4k write, direct IO, 16 jobs, 16 files * 8GiB, iodepth 32, XFS with 16 AGs
Base: 41 KIOPS
With generic ag-set: 93 KIOPS (+126%)
With write-stream ag-set: 227 KIOPS (+453%)
2. On FDP-capable NVMe:
RocksDB YCSB
WAF (base vs write-stream): 35% Reduction
[1]
### Application interface
New vfs ioctl 'FS_IOC_WRITE_STEAM'.
Application communicates the intended operation using the 'op_flags'
field of the passed 'struct fs_write_stream'.
Valid flags are:
FS_WRITE_STREAM_OP_GET_MAX: Returns the number of available streams.
FS_WRITE_STREAM_OP_SET: Assign a specific stream value to the file.
FS_WRITE_STREAM_OP_GET: Query what stream value is set on the file.
[2]
### Comparison with Write Hints (RWH_WRITE_LIFE_*)
- Semantics: Write Hints describe 'data temperature' (e.g.,
short/long/extreme), implying a lifetime. Write Streams describe 'data
placement' (e.g., Bin 1/Bin 2), implying only separation.
- Scalability: Write Hints are limited to a small, fixed enum (6
values). Write streams are dynamic, provider-dependent values that can
scale much higher (kernel limit: up to 255 due to u8 field).
- Discovery: The existing write-hint interface is advisory and decoupled
from underlying capabilties; application has no way to probe support
and cannot deterministically know which hints are valid. OTOH, write-streams
provide explicit discovery.
Note: within the kernel, the separation between two constructs
(write-hint and write-stream) had started from 6.16 itself.
### Changelog
since v2:
https://lore.kernel.org/linux-fsdevel/20260309052944.156054-1-joshi.k@samsung.com/
- xfs default allocator optimization using fixed-size generic AG set (Dave)
- reuse the above to simplify the write-stream AG set handling
- streamline the uapi; Use union for GET_MAX and GET/SET (Darrick)
- uint16_t for write-stream within xfs inode and other cleanups (Darrick)
since v1:
https://lore.kernel.org/linux-fsdevel/20260216052540.217920-1-joshi.k@samsung.com/
- swich from fcntl based to ioctl-based interface (Christian)
- new patch (#4) that makes xfs allocator use the write streams for AG
selection
- new patch (#5) that introduces software write streams in xfs.
### Interface example
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <string.h>
#include <errno.h>
/* Duplicate the kernel UAPI definitions */
struct fs_write_stream {
uint32_t op_flags;
union {
uint32_t stream_id;
uint32_t max_streams;
};
uint64_t __reserved;
};
#define FS_WRITE_STREAM_OP_GET (1 << 1)
#define FS_WRITE_STREAM_OP_SET (1 << 2)
#define FS_WRITE_STREAM_OP_GET_MAX (1 << 0)
#define FS_IOC_WRITE_STREAM _IOWR('f', 135, struct fs_write_stream)
void print_usage(const char *progname) {
fprintf(stderr, "Usage:\n");
fprintf(stderr, " %s <file> max - Get max supported streams\n", progname);
fprintf(stderr, " %s <file> get - Get current stream ID\n", progname);
fprintf(stderr, " %s <file> set <id> - Set stream ID\n", progname);
exit(EXIT_FAILURE);
}
int main(int argc, char *argv[]) {
if (argc < 3)
print_usage(argv[0]);
const char *filepath = argv[1];
const char *cmd = argv[2];
int fd = open(filepath, O_RDWR);
if (fd < 0) {
perror("Error opening file");
return EXIT_FAILURE;
}
struct fs_write_stream req;
memset(&req, 0, sizeof(req));
if (strcmp(cmd, "max") == 0) {
req.op_flags = FS_WRITE_STREAM_OP_GET_MAX;
if (ioctl(fd, FS_IOC_WRITE_STREAM, &req) < 0) {
perror("ioctl(GET_MAX) failed");
close(fd);
return EXIT_FAILURE;
}
printf("Max streams supported: %u\n", req.max_streams);
} else if (strcmp(cmd, "get") == 0) {
req.op_flags = FS_WRITE_STREAM_OP_GET;
if (ioctl(fd, FS_IOC_WRITE_STREAM, &req) < 0) {
perror("ioctl(GET) failed");
close(fd);
return EXIT_FAILURE;
}
printf("Current stream ID: %u\n", req.stream_id);
} else if (strcmp(cmd, "set") == 0) {
if (argc != 4)
print_usage(argv[0]);
req.op_flags = FS_WRITE_STREAM_OP_SET;
req.stream_id = atoi(argv[3]);
if (ioctl(fd, FS_IOC_WRITE_STREAM, &req) < 0) {
perror("ioctl(SET) failed");
close(fd);
return EXIT_FAILURE;
}
printf("Set stream ID to: %u\n", req.stream_id);
} else {
fprintf(stderr, "Unknown command: %s\n", cmd);
close(fd);
print_usage(argv[0]);
}
close(fd);
return EXIT_SUCCESS;
}
Kanchan Joshi (6):
fs: add generic write-stream management ioctl
iomap: introduce and propagate write_stream
xfs: implement write-stream management support
xfs: generic AG set based steering
xfs: write stream based AG placement
xfs: introduce software write streams
fs/iomap/direct-io.c | 1 +
fs/iomap/ioend.c | 3 ++
fs/xfs/libxfs/xfs_bmap.c | 74 ++++++++++++++++++++++++++++++++++++++++
fs/xfs/xfs_icache.c | 1 +
fs/xfs/xfs_inode.c | 69 +++++++++++++++++++++++++++++++++++++
fs/xfs/xfs_inode.h | 6 ++++
fs/xfs/xfs_ioctl.c | 38 +++++++++++++++++++++
fs/xfs/xfs_iomap.c | 1 +
include/linux/iomap.h | 2 ++
include/uapi/linux/fs.h | 14 ++++++++
10 files changed, 209 insertions(+)
--
2.25.1
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH v3 1/6] fs: add generic write-stream management ioctl
2026-06-16 18:05 ` [PATCH v3 0/6] xfs write streams Kanchan Joshi
@ 2026-06-16 18:05 ` Kanchan Joshi
2026-06-24 18:03 ` Darrick J. Wong
2026-06-16 18:05 ` [PATCH v3 2/6] iomap: introduce and propagate write_stream Kanchan Joshi
` (4 subsequent siblings)
5 siblings, 1 reply; 10+ messages in thread
From: Kanchan Joshi @ 2026-06-16 18:05 UTC (permalink / raw)
To: brauner, hch, djwong, dgc, jack, cem, axboe, kbusch, ritesh.list
Cc: linux-xfs, linux-fsdevel, linux-block, gost.dev, Kanchan Joshi
Wire up the userspace interface for write stream management via a new
vfs ioctl 'FS_IOC_WRITE_STEAM'.
Application communictes the intended operation using the 'op_flags'
field of the passed 'struct fs_write_stream'.
Valid flags are:
FS_WRITE_STREAM_OP_GET_MAX: Returns the number of available streams.
FS_WRITE_STREAM_OP_SET: Assign a specific stream value to the file.
FS_WRITE_STREAM_OP_GET: Query what stream value is set on the file.
Application should query the available streams by using
FS_WRITE_STREAM_OP_GET_MAX first.
If returned value is N, valid stream values for the file are 0 to N.
Stream value 0 implies that no stream is set on the file.
Setting a larger value than available streams is rejected.
Signed-off-by: Kanchan Joshi <joshi.k@samsung.com>
---
include/uapi/linux/fs.h | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/include/uapi/linux/fs.h b/include/uapi/linux/fs.h
index 13f71202845e..9e87271e610b 100644
--- a/include/uapi/linux/fs.h
+++ b/include/uapi/linux/fs.h
@@ -338,6 +338,20 @@ struct file_attr {
/* Get logical block metadata capability details */
#define FS_IOC_GETLBMD_CAP _IOWR(0x15, 2, struct logical_block_metadata_cap)
+struct fs_write_stream {
+ __u32 op_flags; /* IN: operation flags */
+ union {
+ __u32 stream_id; /* IN/OUT: stream value to assign/guery */
+ __u32 max_streams; /* OUT: max streams values supported */
+ };
+ __u64 rsvd;
+};
+
+#define FS_WRITE_STREAM_OP_GET_MAX (1 << 0)
+#define FS_WRITE_STREAM_OP_GET (1 << 1)
+#define FS_WRITE_STREAM_OP_SET (1 << 2)
+
+#define FS_IOC_WRITE_STREAM _IOWR('f', 135, struct fs_write_stream)
/*
* Inode flags (FS_IOC_GETFLAGS / FS_IOC_SETFLAGS)
*
--
2.25.1
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH v3 2/6] iomap: introduce and propagate write_stream
2026-06-16 18:05 ` [PATCH v3 0/6] xfs write streams Kanchan Joshi
2026-06-16 18:05 ` [PATCH v3 1/6] fs: add generic write-stream management ioctl Kanchan Joshi
@ 2026-06-16 18:05 ` Kanchan Joshi
2026-06-24 18:10 ` Darrick J. Wong
2026-06-16 18:05 ` [PATCH v3 3/6] xfs: implement write-stream management support Kanchan Joshi
` (3 subsequent siblings)
5 siblings, 1 reply; 10+ messages in thread
From: Kanchan Joshi @ 2026-06-16 18:05 UTC (permalink / raw)
To: brauner, hch, djwong, dgc, jack, cem, axboe, kbusch, ritesh.list
Cc: linux-xfs, linux-fsdevel, linux-block, gost.dev, Kanchan Joshi
Add a new write_stream field to struct iomap. Existing hole is used to
place the new field.
Propagate write_stream from iomap to bio in both direct I/O and buffered
writeback paths.
Signed-off-by: Kanchan Joshi <joshi.k@samsung.com>
---
fs/iomap/direct-io.c | 1 +
fs/iomap/ioend.c | 3 +++
include/linux/iomap.h | 2 ++
3 files changed, 6 insertions(+)
diff --git a/fs/iomap/direct-io.c b/fs/iomap/direct-io.c
index b36ee619cdcd..455fd5d97d25 100644
--- a/fs/iomap/direct-io.c
+++ b/fs/iomap/direct-io.c
@@ -348,6 +348,7 @@ static ssize_t iomap_dio_bio_iter_one(struct iomap_iter *iter,
fscrypt_set_bio_crypt_ctx(bio, iter->inode, pos, GFP_KERNEL);
bio->bi_iter.bi_sector = iomap_sector(&iter->iomap, pos);
bio->bi_write_hint = iter->inode->i_write_hint;
+ bio->bi_write_stream = iter->iomap.write_stream;
bio->bi_ioprio = dio->iocb->ki_ioprio;
bio->bi_private = dio;
bio->bi_end_io = iomap_dio_bio_end_io;
diff --git a/fs/iomap/ioend.c b/fs/iomap/ioend.c
index acf3cf98b23a..56ed5ba6a421 100644
--- a/fs/iomap/ioend.c
+++ b/fs/iomap/ioend.c
@@ -164,6 +164,7 @@ static struct iomap_ioend *iomap_alloc_ioend(struct iomap_writepage_ctx *wpc,
GFP_NOFS, &iomap_ioend_bioset);
bio->bi_iter.bi_sector = iomap_sector(&wpc->iomap, pos);
bio->bi_write_hint = wpc->inode->i_write_hint;
+ bio->bi_write_stream = wpc->iomap.write_stream;
wbc_init_bio(wpc->wbc, bio);
wpc->nr_folios = 0;
return iomap_init_ioend(wpc->inode, bio, pos, ioend_flags);
@@ -187,6 +188,8 @@ static bool iomap_can_add_to_ioend(struct iomap_writepage_ctx *wpc, loff_t pos,
if (!(wpc->iomap.flags & IOMAP_F_ANON_WRITE) &&
iomap_sector(&wpc->iomap, pos) != bio_end_sector(&ioend->io_bio))
return false;
+ if (wpc->iomap.write_stream != ioend->io_bio.bi_write_stream)
+ return false;
/*
* Limit ioend bio chain lengths to minimise IO completion latency. This
* also prevents long tight loops ending page writeback on all the
diff --git a/include/linux/iomap.h b/include/linux/iomap.h
index 2c5685adf3a9..44583429ffa4 100644
--- a/include/linux/iomap.h
+++ b/include/linux/iomap.h
@@ -120,6 +120,8 @@ struct iomap {
u64 length; /* length of mapping, bytes */
u16 type; /* type of mapping */
u16 flags; /* flags for mapping */
+ u8 write_stream; /* write stream for I/O */
+ /* 3 bytes padding hole here */
struct block_device *bdev; /* block device for I/O */
struct dax_device *dax_dev; /* dax_dev for dax operations */
void *inline_data;
--
2.25.1
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH v3 3/6] xfs: implement write-stream management support
2026-06-16 18:05 ` [PATCH v3 0/6] xfs write streams Kanchan Joshi
2026-06-16 18:05 ` [PATCH v3 1/6] fs: add generic write-stream management ioctl Kanchan Joshi
2026-06-16 18:05 ` [PATCH v3 2/6] iomap: introduce and propagate write_stream Kanchan Joshi
@ 2026-06-16 18:05 ` Kanchan Joshi
2026-06-24 18:11 ` Darrick J. Wong
2026-06-16 18:05 ` [PATCH v3 4/6] xfs: generic AG set based steering Kanchan Joshi
` (2 subsequent siblings)
5 siblings, 1 reply; 10+ messages in thread
From: Kanchan Joshi @ 2026-06-16 18:05 UTC (permalink / raw)
To: brauner, hch, djwong, dgc, jack, cem, axboe, kbusch, ritesh.list
Cc: linux-xfs, linux-fsdevel, linux-block, gost.dev, Kanchan Joshi
Implement support for FS_IOC_WRITE_STREAM ioctl.
For FS_WRITE_STREAM_OP_GET_MAX, available write streams are reported
based on the capability of the underlying block device.
For FS_WRITE_STREAM_OP_{SET/GET}, add a new i_write_stream field in xfs
inode. This value is propagated to the iomap during block mapping.
Signed-off-by: Kanchan Joshi <joshi.k@samsung.com>
---
fs/xfs/xfs_icache.c | 1 +
fs/xfs/xfs_inode.c | 46 +++++++++++++++++++++++++++++++++++++++++++++
fs/xfs/xfs_inode.h | 6 ++++++
fs/xfs/xfs_ioctl.c | 38 +++++++++++++++++++++++++++++++++++++
fs/xfs/xfs_iomap.c | 1 +
5 files changed, 92 insertions(+)
diff --git a/fs/xfs/xfs_icache.c b/fs/xfs/xfs_icache.c
index 2040a9292ee6..d5f880f5b810 100644
--- a/fs/xfs/xfs_icache.c
+++ b/fs/xfs/xfs_icache.c
@@ -130,6 +130,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 beaa26ec62da..2e7c61d71b48 100644
--- a/fs/xfs/xfs_inode.c
+++ b/fs/xfs/xfs_inode.c
@@ -47,6 +47,52 @@
struct kmem_cache *xfs_inode_cache;
+int
+xfs_inode_max_write_streams(
+ struct xfs_inode *ip)
+{
+ struct block_device *bdev;
+
+ bdev = xfs_inode_buftarg(ip)->bt_bdev;
+ if (!bdev)
+ 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;
+}
+
+int
+xfs_inode_set_write_stream(
+ struct xfs_inode *ip,
+ uint16_t stream_id)
+{
+ int ret = 0;
+
+ xfs_ilock(ip, XFS_ILOCK_EXCL);
+
+ if (stream_id > xfs_inode_max_write_streams(ip)) {
+ ret = -EINVAL;
+ goto out_unlock;
+ }
+ ip->i_write_stream = 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 bd6d33557194..768c4195306c 100644
--- a/fs/xfs/xfs_inode.h
+++ b/fs/xfs/xfs_inode.h
@@ -38,6 +38,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 */
@@ -676,4 +679,7 @@ 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_set_write_stream(struct xfs_inode *ip, uint16_t stream_id);
#endif /* __XFS_INODE_H__ */
diff --git a/fs/xfs/xfs_ioctl.c b/fs/xfs/xfs_ioctl.c
index 46e234863644..3f82a4884b81 100644
--- a/fs/xfs/xfs_ioctl.c
+++ b/fs/xfs/xfs_ioctl.c
@@ -1179,6 +1179,42 @@ xfs_ioctl_fs_counts(
return 0;
}
+static int
+xfs_ioc_write_stream(
+ struct file *filp,
+ void __user *arg)
+{
+ struct inode *inode = file_inode(filp);
+ struct xfs_inode *ip = XFS_I(inode);
+ struct fs_write_stream ws = { };
+
+ if (copy_from_user(&ws, arg, sizeof(ws)))
+ return -EFAULT;
+ if (ws.rsvd != 0)
+ return -EINVAL;
+
+ switch (ws.op_flags) {
+ case FS_WRITE_STREAM_OP_GET_MAX:
+ ws.max_streams = xfs_inode_max_write_streams(ip);
+ goto copy_out;
+ case FS_WRITE_STREAM_OP_GET:
+ ws.stream_id = xfs_inode_get_write_stream(ip);
+ goto copy_out;
+ case FS_WRITE_STREAM_OP_SET:
+ if (!(filp->f_mode & FMODE_WRITE))
+ return -EBADF;
+ return xfs_inode_set_write_stream(ip, ws.stream_id);
+ default:
+ return -EINVAL;
+ }
+ return 0;
+
+copy_out:
+ if (copy_to_user(arg, &ws, sizeof(ws)))
+ return -EFAULT;
+ return 0;
+}
+
/*
* 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.
@@ -1444,6 +1480,8 @@ 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:
+ return xfs_ioc_write_stream(filp, arg);
default:
return -ENOTTY;
diff --git a/fs/xfs/xfs_iomap.c b/fs/xfs/xfs_iomap.c
index f20a02f49ed9..ccbf7dcf1ad5 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->flags = iomap_flags;
+ iomap->write_stream = ip->i_write_stream;
if (mapping_flags & IOMAP_DAX) {
iomap->dax_dev = target->bt_daxdev;
} else {
--
2.25.1
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH v3 4/6] xfs: generic AG set based steering
2026-06-16 18:05 ` [PATCH v3 0/6] xfs write streams Kanchan Joshi
` (2 preceding siblings ...)
2026-06-16 18:05 ` [PATCH v3 3/6] xfs: implement write-stream management support Kanchan Joshi
@ 2026-06-16 18:05 ` Kanchan Joshi
2026-06-16 18:05 ` [PATCH v3 5/6] xfs: write stream based AG placement Kanchan Joshi
2026-06-16 18:05 ` [PATCH v3 6/6] xfs: introduce software write streams Kanchan Joshi
5 siblings, 0 replies; 10+ messages in thread
From: Kanchan Joshi @ 2026-06-16 18:05 UTC (permalink / raw)
To: brauner, hch, djwong, dgc, jack, cem, axboe, kbusch, ritesh.list
Cc: linux-xfs, linux-fsdevel, linux-block, gost.dev, Kanchan Joshi,
Anuj Gupta
Improve allocator concurrency and reduce interlaving by introducing
fixed sized AG set.
Use low bits of the inode as a hash to select AG within the AG set.
Overall, a file will try to use the same AG (and contiguity is maintained),
but multiple files will be spread across all AGs in the target AG set.
Suggested-by: Dave Chinner <dgc@kernel.org>
Signed-off-by: Kanchan Joshi <joshi.k@samsung.com>
Signed-off-by: Anuj Gupta <anuj20.g@samsung.com>
---
fs/xfs/libxfs/xfs_bmap.c | 38 ++++++++++++++++++++++++++++++++++++++
1 file changed, 38 insertions(+)
diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c
index 7a4c8f1aa76c..6685220ec59a 100644
--- a/fs/xfs/libxfs/xfs_bmap.c
+++ b/fs/xfs/libxfs/xfs_bmap.c
@@ -3194,6 +3194,36 @@ xfs_bmap_select_minlen(
return args->maxlen;
}
+#define GENERIC_AG_SET_SZ (2)
+
+static inline xfs_agnumber_t
+xfs_default_ag_set_size(
+ struct xfs_inode *ip)
+{
+ struct xfs_mount *mp = ip->i_mount;
+
+ return min_t(xfs_agnumber_t, GENERIC_AG_SET_SZ, mp->m_sb.sb_agcount);
+}
+
+static xfs_agnumber_t
+xfs_ag_to_ag_set(
+ struct xfs_bmalloca *ap,
+ xfs_agnumber_t base_agno)
+{
+ struct xfs_inode *ip = ap->ip;
+ struct xfs_mount *mp = ip->i_mount;
+ xfs_agnumber_t set_size;
+
+ /* Apply fanning only for regular file data */
+ if (!(ap->datatype & XFS_ALLOC_USERDATA))
+ return base_agno;
+
+ set_size = xfs_default_ag_set_size(ip);
+ /* Fan out within the AG set using low bits of the inode */
+ return (base_agno + (XFS_INO_TO_AGINO(mp, ip->i_ino) % set_size)) %
+ mp->m_sb.sb_agcount;
+}
+
static int
xfs_bmap_btalloc_select_lengths(
struct xfs_bmalloca *ap,
@@ -3589,8 +3619,16 @@ xfs_bmap_btalloc_best_length(
{
xfs_extlen_t blen = 0;
int error;
+ xfs_agnumber_t target_ag, start_ag;
ap->blkno = XFS_INO_TO_FSB(args->mp, ap->ip->i_ino);
+
+ /* fan out initial AG across the generic AG set */
+ start_ag = XFS_FSB_TO_AGNO(args->mp, ap->blkno);
+ target_ag = xfs_ag_to_ag_set(ap, start_ag);
+ if (target_ag != start_ag)
+ ap->blkno = XFS_AGB_TO_FSB(args->mp, target_ag, 0);
+
if (!xfs_bmap_adjacent(ap))
ap->eof = false;
--
2.25.1
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH v3 5/6] xfs: write stream based AG placement
2026-06-16 18:05 ` [PATCH v3 0/6] xfs write streams Kanchan Joshi
` (3 preceding siblings ...)
2026-06-16 18:05 ` [PATCH v3 4/6] xfs: generic AG set based steering Kanchan Joshi
@ 2026-06-16 18:05 ` Kanchan Joshi
2026-06-16 18:05 ` [PATCH v3 6/6] xfs: introduce software write streams Kanchan Joshi
5 siblings, 0 replies; 10+ messages in thread
From: Kanchan Joshi @ 2026-06-16 18:05 UTC (permalink / raw)
To: brauner, hch, djwong, dgc, jack, cem, axboe, kbusch, ritesh.list
Cc: linux-xfs, linux-fsdevel, linux-block, gost.dev, Kanchan Joshi
When write stream is set on the file, choose the AG set based on the
write stream value.
Isolating distinct write streams into dedicated allocation groups helps
in reducing the block interleaving of concurrent writers. Keeping these
streams spatially separated reduces AGF lock contention and logical file
fragmentation.
If AGs are fewer than write streams, write streams are distributed into
available AGs in round robin fashion.
If not, available AGs are partitioned into write streams. The write-stream
value is used to derive the AG set, and low bits of the inode is used to
derive the AG within the AG set.
While each stream provides the isolation, the intra-stream concurrency
comes from the AG set size.
Example: 8 Allocation Groups, 4 write streams
AG set size = 2 AGs per write stream
Stream 1 (ID: 1) Stream 2 (ID: 2) Streams 3 & 4
+---------+---------+ +---------+---------+ +-------------
| AG0 | AG1 | | AG2 | AG3 | | AG4...AG7
+---------+---------+ +---------+---------+ +-------------
^ ^ ^ ^
| | | |
| File B (ino: 101) | File D (ino: 201)
| 101 % 2 = 1 -> AG 1 | 201 % 2 = 1 -> AG 3
| |
File A (ino: 100) File C (ino: 200)
100 % 2 = 0 -> AG 0 200 % 2 = 0 -> AG 2
If AGs can not be evenly distributed among streams, the last stream will
absorb the remaining AGs.
Note that there are no hard boundaries; this only provides explicit
routing hint to xfs allocator. We still preserve file contiguity, and the
full space can be utilized even with a single stream.
Signed-off-by: Kanchan Joshi <joshi.k@samsung.com>
---
fs/xfs/libxfs/xfs_bmap.c | 38 +++++++++++++++++++++++++++++++++++++-
1 file changed, 37 insertions(+), 1 deletion(-)
diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c
index 6685220ec59a..325987b5bd9e 100644
--- a/fs/xfs/libxfs/xfs_bmap.c
+++ b/fs/xfs/libxfs/xfs_bmap.c
@@ -3205,6 +3205,38 @@ xfs_default_ag_set_size(
return min_t(xfs_agnumber_t, GENERIC_AG_SET_SZ, mp->m_sb.sb_agcount);
}
+static xfs_agnumber_t
+xfs_inode_write_stream_ag_set(
+ struct xfs_inode *ip,
+ xfs_agnumber_t *target_agno)
+{
+ struct xfs_mount *mp = ip->i_mount;
+ uint32_t nr_streams = xfs_inode_max_write_streams(ip);
+ uint32_t stream_id = ip->i_write_stream;
+ uint32_t nr_ags = mp->m_sb.sb_agcount;
+ xfs_agnumber_t set_size;
+
+
+ if (!nr_streams)
+ return xfs_default_ag_set_size(ip);
+
+ stream_id -= 1; /* For 0-based math; stream-ids are 1-based */
+ set_size = nr_ags / nr_streams;
+
+ if (set_size) {
+ *target_agno = stream_id * set_size;
+ /* unven distribution, last stream will cover extra AGs */
+ if (stream_id == nr_streams - 1)
+ set_size = nr_ags - *target_agno;
+ } else {
+ /* for the case when we have fewer AGs than streams */
+ *target_agno = stream_id % nr_ags;
+ set_size = 1;
+ }
+
+ return set_size;
+}
+
static xfs_agnumber_t
xfs_ag_to_ag_set(
struct xfs_bmalloca *ap,
@@ -3218,7 +3250,11 @@ xfs_ag_to_ag_set(
if (!(ap->datatype & XFS_ALLOC_USERDATA))
return base_agno;
- set_size = xfs_default_ag_set_size(ip);
+ if (ip->i_write_stream)
+ set_size = xfs_inode_write_stream_ag_set(ip, &base_agno);
+ else
+ set_size = xfs_default_ag_set_size(ip);
+
/* Fan out within the AG set using low bits of the inode */
return (base_agno + (XFS_INO_TO_AGINO(mp, ip->i_ino) % set_size)) %
mp->m_sb.sb_agcount;
--
2.25.1
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH v3 6/6] xfs: introduce software write streams
2026-06-16 18:05 ` [PATCH v3 0/6] xfs write streams Kanchan Joshi
` (4 preceding siblings ...)
2026-06-16 18:05 ` [PATCH v3 5/6] xfs: write stream based AG placement Kanchan Joshi
@ 2026-06-16 18:05 ` Kanchan Joshi
5 siblings, 0 replies; 10+ messages in thread
From: Kanchan Joshi @ 2026-06-16 18:05 UTC (permalink / raw)
To: brauner, hch, djwong, dgc, jack, cem, axboe, kbusch, ritesh.list
Cc: linux-xfs, linux-fsdevel, linux-block, gost.dev, Kanchan Joshi
Even when the underlying block device does not advertise write streams,
XFS can choose do so, as that enables logical spatial isolation and
dynamic AG-set based concurrency for the standard storage, excluding
rtvolume.
Use AG count based heuristic to derive AG set size and software streams.
Larger filesystem (i.e., more AGs) get wider fanout (i.e., larger AG-set).
Signed-off-by: Kanchan Joshi <joshi.k@samsung.com>
---
fs/xfs/xfs_inode.c | 25 ++++++++++++++++++++++++-
1 file changed, 24 insertions(+), 1 deletion(-)
diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c
index 2e7c61d71b48..10ffed130dce 100644
--- a/fs/xfs/xfs_inode.c
+++ b/fs/xfs/xfs_inode.c
@@ -45,6 +45,7 @@
#include "xfs_inode_util.h"
#include "xfs_metafile.h"
+#define XFS_MAX_USER_WRITE_STREAMS (16)
struct kmem_cache *xfs_inode_cache;
int
@@ -52,12 +53,34 @@ xfs_inode_max_write_streams(
struct xfs_inode *ip)
{
struct block_device *bdev;
+ struct xfs_mount *mp = ip->i_mount;
+ int nr_streams;
+ xfs_agnumber_t nr_ags, ag_set_size;
bdev = xfs_inode_buftarg(ip)->bt_bdev;
if (!bdev)
return 0;
- return bdev_max_write_streams(bdev);
+ nr_streams = bdev_max_write_streams(bdev);
+ if (nr_streams > 0)
+ return nr_streams;
+ if (XFS_IS_REALTIME_INODE(ip))
+ return 0;
+ /*
+ * Enable software-only streams if hardware streams are not available.
+ * This helps to
+ * - improve isolation; reduce allocation interleaving.
+ * - improve concurrency using AG-set based steering within and across streams.
+ */
+ nr_ags = mp->m_sb.sb_agcount;
+ if (nr_ags >= 32)
+ ag_set_size = 4;
+ else if (nr_ags >= 8)
+ ag_set_size = 2;
+ else
+ ag_set_size = 1;
+ nr_streams = nr_ags / ag_set_size;
+ return min_t(uint16_t, nr_streams, XFS_MAX_USER_WRITE_STREAMS);
}
uint16_t
--
2.25.1
^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [PATCH v3 1/6] fs: add generic write-stream management ioctl
2026-06-16 18:05 ` [PATCH v3 1/6] fs: add generic write-stream management ioctl Kanchan Joshi
@ 2026-06-24 18:03 ` Darrick J. Wong
0 siblings, 0 replies; 10+ messages in thread
From: Darrick J. Wong @ 2026-06-24 18:03 UTC (permalink / raw)
To: Kanchan Joshi
Cc: brauner, hch, dgc, jack, cem, axboe, kbusch, ritesh.list,
linux-xfs, linux-fsdevel, linux-block, gost.dev
On Tue, Jun 16, 2026 at 11:35:50PM +0530, Kanchan Joshi wrote:
> Wire up the userspace interface for write stream management via a new
> vfs ioctl 'FS_IOC_WRITE_STEAM'.
> Application communictes the intended operation using the 'op_flags'
> field of the passed 'struct fs_write_stream'.
> Valid flags are:
> FS_WRITE_STREAM_OP_GET_MAX: Returns the number of available streams.
> FS_WRITE_STREAM_OP_SET: Assign a specific stream value to the file.
> FS_WRITE_STREAM_OP_GET: Query what stream value is set on the file.
>
> Application should query the available streams by using
> FS_WRITE_STREAM_OP_GET_MAX first.
> If returned value is N, valid stream values for the file are 0 to N.
> Stream value 0 implies that no stream is set on the file.
You might want to make that an explicit #define then.
> Setting a larger value than available streams is rejected.
>
> Signed-off-by: Kanchan Joshi <joshi.k@samsung.com>
> ---
> include/uapi/linux/fs.h | 14 ++++++++++++++
> 1 file changed, 14 insertions(+)
>
> diff --git a/include/uapi/linux/fs.h b/include/uapi/linux/fs.h
> index 13f71202845e..9e87271e610b 100644
> --- a/include/uapi/linux/fs.h
> +++ b/include/uapi/linux/fs.h
> @@ -338,6 +338,20 @@ struct file_attr {
> /* Get logical block metadata capability details */
> #define FS_IOC_GETLBMD_CAP _IOWR(0x15, 2, struct logical_block_metadata_cap)
>
> +struct fs_write_stream {
> + __u32 op_flags; /* IN: operation flags */
> + union {
> + __u32 stream_id; /* IN/OUT: stream value to assign/guery */
"query"?
--D
> + __u32 max_streams; /* OUT: max streams values supported */
> + };
> + __u64 rsvd;
> +};
> +
> +#define FS_WRITE_STREAM_OP_GET_MAX (1 << 0)
> +#define FS_WRITE_STREAM_OP_GET (1 << 1)
> +#define FS_WRITE_STREAM_OP_SET (1 << 2)
> +
> +#define FS_IOC_WRITE_STREAM _IOWR('f', 135, struct fs_write_stream)
> /*
> * Inode flags (FS_IOC_GETFLAGS / FS_IOC_SETFLAGS)
> *
> --
> 2.25.1
>
>
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v3 2/6] iomap: introduce and propagate write_stream
2026-06-16 18:05 ` [PATCH v3 2/6] iomap: introduce and propagate write_stream Kanchan Joshi
@ 2026-06-24 18:10 ` Darrick J. Wong
0 siblings, 0 replies; 10+ messages in thread
From: Darrick J. Wong @ 2026-06-24 18:10 UTC (permalink / raw)
To: Kanchan Joshi
Cc: brauner, hch, dgc, jack, cem, axboe, kbusch, ritesh.list,
linux-xfs, linux-fsdevel, linux-block, gost.dev
On Tue, Jun 16, 2026 at 11:35:51PM +0530, Kanchan Joshi wrote:
> Add a new write_stream field to struct iomap. Existing hole is used to
> place the new field.
> Propagate write_stream from iomap to bio in both direct I/O and buffered
> writeback paths.
>
> Signed-off-by: Kanchan Joshi <joshi.k@samsung.com>
> ---
> fs/iomap/direct-io.c | 1 +
> fs/iomap/ioend.c | 3 +++
> include/linux/iomap.h | 2 ++
> 3 files changed, 6 insertions(+)
>
> diff --git a/fs/iomap/direct-io.c b/fs/iomap/direct-io.c
> index b36ee619cdcd..455fd5d97d25 100644
> --- a/fs/iomap/direct-io.c
> +++ b/fs/iomap/direct-io.c
> @@ -348,6 +348,7 @@ static ssize_t iomap_dio_bio_iter_one(struct iomap_iter *iter,
> fscrypt_set_bio_crypt_ctx(bio, iter->inode, pos, GFP_KERNEL);
> bio->bi_iter.bi_sector = iomap_sector(&iter->iomap, pos);
> bio->bi_write_hint = iter->inode->i_write_hint;
> + bio->bi_write_stream = iter->iomap.write_stream;
> bio->bi_ioprio = dio->iocb->ki_ioprio;
> bio->bi_private = dio;
> bio->bi_end_io = iomap_dio_bio_end_io;
> diff --git a/fs/iomap/ioend.c b/fs/iomap/ioend.c
> index acf3cf98b23a..56ed5ba6a421 100644
> --- a/fs/iomap/ioend.c
> +++ b/fs/iomap/ioend.c
> @@ -164,6 +164,7 @@ static struct iomap_ioend *iomap_alloc_ioend(struct iomap_writepage_ctx *wpc,
> GFP_NOFS, &iomap_ioend_bioset);
> bio->bi_iter.bi_sector = iomap_sector(&wpc->iomap, pos);
> bio->bi_write_hint = wpc->inode->i_write_hint;
> + bio->bi_write_stream = wpc->iomap.write_stream;
> wbc_init_bio(wpc->wbc, bio);
> wpc->nr_folios = 0;
> return iomap_init_ioend(wpc->inode, bio, pos, ioend_flags);
> @@ -187,6 +188,8 @@ static bool iomap_can_add_to_ioend(struct iomap_writepage_ctx *wpc, loff_t pos,
> if (!(wpc->iomap.flags & IOMAP_F_ANON_WRITE) &&
> iomap_sector(&wpc->iomap, pos) != bio_end_sector(&ioend->io_bio))
> return false;
> + if (wpc->iomap.write_stream != ioend->io_bio.bi_write_stream)
> + return false;
> /*
> * Limit ioend bio chain lengths to minimise IO completion latency. This
> * also prevents long tight loops ending page writeback on all the
> diff --git a/include/linux/iomap.h b/include/linux/iomap.h
> index 2c5685adf3a9..44583429ffa4 100644
> --- a/include/linux/iomap.h
> +++ b/include/linux/iomap.h
> @@ -120,6 +120,8 @@ struct iomap {
> u64 length; /* length of mapping, bytes */
> u16 type; /* type of mapping */
> u16 flags; /* flags for mapping */
> + u8 write_stream; /* write stream for I/O */
I'm mildly confused by the types here -- the ioctl exposes a u32, iomap
has a u8, and xfs seems to use u16. I gather you want maximum
flexibility in the uapi and that's the reason for the u32, but can the
internal interfaces be made consistent?
I also wonder what happens if the write stream ever becomes persistent,
but this patchset doesn't go there, and maybe the programming model is
simply that you have to set it every time you open the file?
--D
> + /* 3 bytes padding hole here */
> struct block_device *bdev; /* block device for I/O */
> struct dax_device *dax_dev; /* dax_dev for dax operations */
> void *inline_data;
> --
> 2.25.1
>
>
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v3 3/6] xfs: implement write-stream management support
2026-06-16 18:05 ` [PATCH v3 3/6] xfs: implement write-stream management support Kanchan Joshi
@ 2026-06-24 18:11 ` Darrick J. Wong
0 siblings, 0 replies; 10+ messages in thread
From: Darrick J. Wong @ 2026-06-24 18:11 UTC (permalink / raw)
To: Kanchan Joshi
Cc: brauner, hch, dgc, jack, cem, axboe, kbusch, ritesh.list,
linux-xfs, linux-fsdevel, linux-block, gost.dev
On Tue, Jun 16, 2026 at 11:35:52PM +0530, Kanchan Joshi wrote:
> Implement support for FS_IOC_WRITE_STREAM ioctl.
>
> For FS_WRITE_STREAM_OP_GET_MAX, available write streams are reported
> based on the capability of the underlying block device.
> For FS_WRITE_STREAM_OP_{SET/GET}, add a new i_write_stream field in xfs
> inode. This value is propagated to the iomap during block mapping.
>
> Signed-off-by: Kanchan Joshi <joshi.k@samsung.com>
> ---
> fs/xfs/xfs_icache.c | 1 +
> fs/xfs/xfs_inode.c | 46 +++++++++++++++++++++++++++++++++++++++++++++
> fs/xfs/xfs_inode.h | 6 ++++++
> fs/xfs/xfs_ioctl.c | 38 +++++++++++++++++++++++++++++++++++++
> fs/xfs/xfs_iomap.c | 1 +
> 5 files changed, 92 insertions(+)
>
> diff --git a/fs/xfs/xfs_icache.c b/fs/xfs/xfs_icache.c
> index 2040a9292ee6..d5f880f5b810 100644
> --- a/fs/xfs/xfs_icache.c
> +++ b/fs/xfs/xfs_icache.c
> @@ -130,6 +130,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 beaa26ec62da..2e7c61d71b48 100644
> --- a/fs/xfs/xfs_inode.c
> +++ b/fs/xfs/xfs_inode.c
> @@ -47,6 +47,52 @@
>
> struct kmem_cache *xfs_inode_cache;
>
> +int
> +xfs_inode_max_write_streams(
> + struct xfs_inode *ip)
> +{
> + struct block_device *bdev;
> +
> + bdev = xfs_inode_buftarg(ip)->bt_bdev;
> + if (!bdev)
> + 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;
> +}
> +
> +int
> +xfs_inode_set_write_stream(
> + struct xfs_inode *ip,
> + uint16_t stream_id)
> +{
> + int ret = 0;
> +
> + xfs_ilock(ip, XFS_ILOCK_EXCL);
> +
> + if (stream_id > xfs_inode_max_write_streams(ip)) {
> + ret = -EINVAL;
> + goto out_unlock;
> + }
> + ip->i_write_stream = 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 bd6d33557194..768c4195306c 100644
> --- a/fs/xfs/xfs_inode.h
> +++ b/fs/xfs/xfs_inode.h
> @@ -38,6 +38,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 */
> @@ -676,4 +679,7 @@ 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_set_write_stream(struct xfs_inode *ip, uint16_t stream_id);
> #endif /* __XFS_INODE_H__ */
> diff --git a/fs/xfs/xfs_ioctl.c b/fs/xfs/xfs_ioctl.c
> index 46e234863644..3f82a4884b81 100644
> --- a/fs/xfs/xfs_ioctl.c
> +++ b/fs/xfs/xfs_ioctl.c
> @@ -1179,6 +1179,42 @@ xfs_ioctl_fs_counts(
> return 0;
> }
>
> +static int
> +xfs_ioc_write_stream(
> + struct file *filp,
> + void __user *arg)
> +{
> + struct inode *inode = file_inode(filp);
> + struct xfs_inode *ip = XFS_I(inode);
> + struct fs_write_stream ws = { };
> +
> + if (copy_from_user(&ws, arg, sizeof(ws)))
> + return -EFAULT;
> + if (ws.rsvd != 0)
> + return -EINVAL;
> +
> + switch (ws.op_flags) {
> + case FS_WRITE_STREAM_OP_GET_MAX:
> + ws.max_streams = xfs_inode_max_write_streams(ip);
Shouldn't you hold ILOCK when you look at the REALTIME bit?
--D
> + goto copy_out;
> + case FS_WRITE_STREAM_OP_GET:
> + ws.stream_id = xfs_inode_get_write_stream(ip);
> + goto copy_out;
> + case FS_WRITE_STREAM_OP_SET:
> + if (!(filp->f_mode & FMODE_WRITE))
> + return -EBADF;
> + return xfs_inode_set_write_stream(ip, ws.stream_id);
> + default:
> + return -EINVAL;
> + }
> + return 0;
> +
> +copy_out:
> + if (copy_to_user(arg, &ws, sizeof(ws)))
> + return -EFAULT;
> + return 0;
> +}
> +
> /*
> * 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.
> @@ -1444,6 +1480,8 @@ 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:
> + return xfs_ioc_write_stream(filp, arg);
>
> default:
> return -ENOTTY;
> diff --git a/fs/xfs/xfs_iomap.c b/fs/xfs/xfs_iomap.c
> index f20a02f49ed9..ccbf7dcf1ad5 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->flags = iomap_flags;
> + iomap->write_stream = ip->i_write_stream;
> if (mapping_flags & IOMAP_DAX) {
> iomap->dax_dev = target->bt_daxdev;
> } else {
> --
> 2.25.1
>
>
^ permalink raw reply [flat|nested] 10+ messages in thread
end of thread, other threads:[~2026-06-24 18:11 UTC | newest]
Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
[not found] <CGME20260616181240epcas5p3f86fbb67f0d04cb0ee4b34839c9522b5@epcas5p3.samsung.com>
2026-06-16 18:05 ` [PATCH v3 0/6] xfs write streams Kanchan Joshi
2026-06-16 18:05 ` [PATCH v3 1/6] fs: add generic write-stream management ioctl Kanchan Joshi
2026-06-24 18:03 ` Darrick J. Wong
2026-06-16 18:05 ` [PATCH v3 2/6] iomap: introduce and propagate write_stream Kanchan Joshi
2026-06-24 18:10 ` Darrick J. Wong
2026-06-16 18:05 ` [PATCH v3 3/6] xfs: implement write-stream management support Kanchan Joshi
2026-06-24 18:11 ` Darrick J. Wong
2026-06-16 18:05 ` [PATCH v3 4/6] xfs: generic AG set based steering Kanchan Joshi
2026-06-16 18:05 ` [PATCH v3 5/6] xfs: write stream based AG placement Kanchan Joshi
2026-06-16 18:05 ` [PATCH v3 6/6] xfs: introduce software write streams Kanchan Joshi
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox