[PATCH v4 0/6] xfs write streams

Kanchan Joshi <[email protected]>
Newsgroups org.kernel.vger.linux-xfs,org.kernel.vger.linux-block,org.kernel.vger.linux-fsdevel
Message-ID <[email protected]>
This series introduces a generic interface [1] for write stream management on
files. It enables spatial isolation and concurrency improvments [3] in xfs using
- generic AG-set (patch #4)
- write-stream based AG-set (patch #5)

In LSFMMBPF'26, we discussed write-stream as a mechanism to reduce
the filesystem allocator bottlenecks and improving direct/buffered IO
scalability.

Write streams allow the abstraction provider (fs, block, 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:
- Since high-level write stream (in xfs) and logical placement can work
  without the low-level write streams (in block device), series has a general
value beyond hardware that provides spatial isolation.

- For hardware-only spatial isolation, 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

a. Inter-stream concurrency
---------------------------
fio: 4k write, direct IO, 16 jobs, 1 directory, 16 files * 8GiB, iodepth 32
xfs: 16 AGs, 4 write-streams

base: 41 KIOPS
generic AG-set: 93 KIOPS (+126%)
write-stream AG-set: 227 KIOPS (+453%)
here, 16 files are assigned 4 unique write-streams (4 files/stream)

b. Intra-stream concurrency
----------------------------
fio: 4k write, direct IO, 4 jobs, 1 directory, 4 files * 8GiB, iodepth 32
xfs: 16 AGs, 4 write-streams, generic AG-set size = 2, write-stream AG-set size = 4

base: 59 KIOPS
generic AG-set: 94 KIOPS (+59%)
write-stream AG-set: 112 KIOPS (+89%)
here, 4 files are assigned single write-stream

2. On FDP-capable NVMe:
RocksDB YCSB
WAF (base vs write-stream): 35% Reduction


[1]
### Application interface

Four new ioctls:
      FS_IOC_WRITE_STREAM_GET_MAX  query the max streams supported
      FS_IOC_WRITE_STREAM_OPEN     open a stream id, returns a stream fd
      FS_IOC_WRITE_STREAM_SET      attach the stream fd to an open file
      FS_IOC_WRITE_STREAM_GET      query the stream id value set on a file

### 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.

- Usage model: application needs to get a handle (fd) for a write stream
  before being able to use it. This avoids multi-application conflicts.

Note: within the kernel, the separation between two constructs
(write-hint and write-stream) had started from 6.16 itself.

### Changelog

since v3:
https://lore.kernel.org/linux-block/[email protected]/
- add fd-based interface to open/set the write stream (Christoph)
- move from single multiplexed ioctl to 4 distinct ioctls (Christoph)
- add mutual exclusion checks against existing write-hint, filestream (Christoph)
- uint16_t for write-stream within iomap and other streamlining (Darrick)

since v2:
https://lore.kernel.org/linux-fsdevel/[email protected]/
- 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/[email protected]/
- 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

/* FD-based write-stream ioctl */

#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>
#include <linux/types.h>

/* Duplicate the kernel UAPI definitions */
struct fs_write_stream_open {
	uint32_t	stream_id;	/* IN: desired id if OPEN_EXACT set; OUT: assigned id */
	uint32_t	flags;		/* IN: FS_WRITE_STREAM_OPEN_* */
};

#define FS_WRITE_STREAM_OPEN_EXACT      (1 << 0)

#define FS_IOC_WRITE_STREAM_GET_MAX     _IOR('f', 135, __u32)
#define FS_IOC_WRITE_STREAM_OPEN        _IOWR('f', 136, struct fs_write_stream_open)
#define FS_IOC_WRITE_STREAM_SET         _IOW('f', 137, __s32)
#define FS_IOC_WRITE_STREAM_GET         _IOR('f', 138, __u32)

static void usage(const char *prog)
{
	fprintf(stderr, "Usage:\n");
	fprintf(stderr, "  %s <file> max        - get max supported streams\n", prog);
	fprintf(stderr, "  %s <file> get        - get stream id set on file\n", prog);
	fprintf(stderr, "  %s <file> open [id]  - open a stream (EXACT if id given) and bind it to file\n", prog);
	exit(EXIT_FAILURE);
}

int main(int argc, char *argv[])
{
	const char *filepath, *cmd;
	int fd;

	if (argc < 3)
		usage(argv[0]);

	filepath = argv[1];
	cmd = argv[2];

	fd = open(filepath, O_RDWR);
	if (fd < 0) {
		perror("open(file)");
		return EXIT_FAILURE;
	}

	if (!strcmp(cmd, "max")) {
		uint32_t max;

		if (ioctl(fd, FS_IOC_WRITE_STREAM_GET_MAX, &max) < 0) {
			perror("ioctl(GET_MAX)");
			return EXIT_FAILURE;
		}
		printf("Max streams supported: %u\n", max);
	} else if (!strcmp(cmd, "get")) {
		uint32_t stream_id;

		if (ioctl(fd, FS_IOC_WRITE_STREAM_GET, &stream_id) < 0) {
			perror("ioctl(GET)");
			return EXIT_FAILURE;
		}
		printf("Stream id on file: %u\n", stream_id);
	} else if (!strcmp(cmd, "open")) {
		struct fs_write_stream_open wso = { 0 };
		int stream_fd;

		if (argc == 4) {
			wso.flags = FS_WRITE_STREAM_OPEN_EXACT;
			wso.stream_id = atoi(argv[3]);
		}

		/* OPEN can be called through any fd on the target filesystem. */
		stream_fd = ioctl(fd, FS_IOC_WRITE_STREAM_OPEN, &wso);
		if (stream_fd < 0) {
			perror("ioctl(OPEN)");
			return EXIT_FAILURE;
		}
		printf("Opened stream id %u (fd %d)\n", wso.stream_id, stream_fd);

		/* SET takes the stream fd directly as the ioctl argument. */
		if (ioctl(fd, FS_IOC_WRITE_STREAM_SET, (unsigned long)stream_fd) < 0) {
			perror("ioctl(SET)");
			close(stream_fd);
			return EXIT_FAILURE;
		}
		printf("Bound stream %u to %s\n", wso.stream_id, filepath);

		close(stream_fd);
	} else {
		fprintf(stderr, "Unknown command: %s\n", cmd);
		usage(argv[0]);
	}

	close(fd);
	return EXIT_SUCCESS;
}


Anuj Gupta (2):
  fs: add write-stream management ioctls
  xfs: implement write-stream management support

Kanchan Joshi (4):
  iomap: introduce and propagate write_stream
  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       | 175 +++++++++++++++++++++++++++++++++++++++
 fs/xfs/xfs_inode.h       |   8 ++
 fs/xfs/xfs_ioctl.c       |  69 +++++++++++++++
 fs/xfs/xfs_iomap.c       |   1 +
 fs/xfs/xfs_mount.h       |   5 ++
 fs/xfs/xfs_super.c       |  12 +++
 include/linux/iomap.h    |   2 +
 include/uapi/linux/fs.h  |  16 ++++
 12 files changed, 367 insertions(+)

-- 
2.25.1
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.