Commit 278c7d9b authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge tag 'vfs-6.17-rc1.fallocate' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs

Pull fallocate updates from Christian Brauner:
 "fallocate() currently supports creating preallocated files
  efficiently. However, on most filesystems fallocate() will preallocate
  blocks in an unwriten state even if FALLOC_FL_ZERO_RANGE is specified.

  The extent state must later be converted to a written state when the
  user writes data into this range, which can trigger numerous metadata
  changes and journal I/O. This may leads to significant write
  amplification and performance degradation in synchronous write mode.

  At the moment, the only method to avoid this is to create an empty
  file and write zero data into it (for example, using 'dd' with a large
  block size). However, this method is slow and consumes a considerable
  amount of disk bandwidth.

  Now that more and more flash-based storage devices are available it is
  possible to efficiently write zeros to SSDs using the unmap write
  zeroes command if the devices do not write physical zeroes to the
  media.

  For example, if SCSI SSDs support the UMMAP bit or NVMe SSDs support
  the DEAC bit[1], the write zeroes command does not write actual data
  to the device, instead, NVMe converts the zeroed range to a
  deallocated state, which works fast and consumes almost no disk write
  bandwidth.

  This series implements the BLK_FEAT_WRITE_ZEROES_UNMAP feature and
  BLK_FLAG_WRITE_ZEROES_UNMAP_DISABLED flag for SCSI, NVMe and
  device-mapper drivers, and add the FALLOC_FL_WRITE_ZEROES and
  STATX_ATTR_WRITE_ZEROES_UNMAP support for ext4 and raw bdev devices.

  fallocate() is subsequently extended with the FALLOC_FL_WRITE_ZEROES
  flag. FALLOC_FL_WRITE_ZEROES zeroes a specified file range in such a
  way that subsequent writes to that range do not require further
  changes to the file mapping metadata. This flag is beneficial for
  subsequent pure overwriting within this range, as it can save on block
  allocation and, consequently, significant metadata changes"

* tag 'vfs-6.17-rc1.fallocate' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs:
  ext4: add FALLOC_FL_WRITE_ZEROES support
  block: add FALLOC_FL_WRITE_ZEROES support
  block: factor out common part in blkdev_fallocate()
  fs: introduce FALLOC_FL_WRITE_ZEROES to fallocate
  dm: clear unmap write zeroes limits when disabling write zeroes
  scsi: sd: set max_hw_wzeroes_unmap_sectors if device supports SD_ZERO_*_UNMAP
  nvmet: set WZDS and DRB if device enables unmap write zeroes operation
  nvme: set max_hw_wzeroes_unmap_sectors if device supports DEAC bit
  block: introduce max_{hw|user}_wzeroes_unmap_sectors to queue limits
parents 0c4ec4a3 4f984fe7
Loading
Loading
Loading
Loading
+33 −0
Original line number Diff line number Diff line
@@ -778,6 +778,39 @@ Description:
		0, write zeroes is not supported by the device.


What:		/sys/block/<disk>/queue/write_zeroes_unmap_max_hw_bytes
Date:		January 2025
Contact:	Zhang Yi <yi.zhang@huawei.com>
Description:
		[RO] This file indicates whether a device supports zeroing data
		in a specified block range without incurring the cost of
		physically writing zeroes to the media for each individual
		block. If this parameter is set to write_zeroes_max_bytes, the
		device implements a zeroing operation which opportunistically
		avoids writing zeroes to media while still guaranteeing that
		subsequent reads from the specified block range will return
		zeroed data. This operation is a best-effort optimization, a
		device may fall back to physically writing zeroes to the media
		due to other factors such as misalignment or being asked to
		clear a block range smaller than the device's internal
		allocation unit. If this parameter is set to 0, the device may
		have to write each logical block media during a zeroing
		operation.


What:		/sys/block/<disk>/queue/write_zeroes_unmap_max_bytes
Date:		January 2025
Contact:	Zhang Yi <yi.zhang@huawei.com>
Description:
		[RW] While write_zeroes_unmap_max_hw_bytes is the hardware limit
		for the device, this setting is the software limit. Since the
		unmap write zeroes operation is a best-effort optimization, some
		devices may still physically writing zeroes to media. So the
		speed of this operation is not guaranteed. Writing a value of
		'0' to this file disables this operation. Otherwise, this
		parameter should be equal to write_zeroes_unmap_max_hw_bytes.


What:		/sys/block/<disk>/queue/zone_append_max_bytes
Date:		May 2020
Contact:	linux-block@vger.kernel.org
+18 −2
Original line number Diff line number Diff line
@@ -50,6 +50,8 @@ void blk_set_stacking_limits(struct queue_limits *lim)
	lim->max_sectors = UINT_MAX;
	lim->max_dev_sectors = UINT_MAX;
	lim->max_write_zeroes_sectors = UINT_MAX;
	lim->max_hw_wzeroes_unmap_sectors = UINT_MAX;
	lim->max_user_wzeroes_unmap_sectors = UINT_MAX;
	lim->max_hw_zone_append_sectors = UINT_MAX;
	lim->max_user_discard_sectors = UINT_MAX;
}
@@ -333,6 +335,12 @@ int blk_validate_limits(struct queue_limits *lim)
	if (!lim->max_segments)
		lim->max_segments = BLK_MAX_SEGMENTS;

	if (lim->max_hw_wzeroes_unmap_sectors &&
	    lim->max_hw_wzeroes_unmap_sectors != lim->max_write_zeroes_sectors)
		return -EINVAL;
	lim->max_wzeroes_unmap_sectors = min(lim->max_hw_wzeroes_unmap_sectors,
			lim->max_user_wzeroes_unmap_sectors);

	lim->max_discard_sectors =
		min(lim->max_hw_discard_sectors, lim->max_user_discard_sectors);

@@ -418,10 +426,11 @@ int blk_set_default_limits(struct queue_limits *lim)
{
	/*
	 * Most defaults are set by capping the bounds in blk_validate_limits,
	 * but max_user_discard_sectors is special and needs an explicit
	 * initialization to the max value here.
	 * but these limits are special and need an explicit initialization to
	 * the max value here.
	 */
	lim->max_user_discard_sectors = UINT_MAX;
	lim->max_user_wzeroes_unmap_sectors = UINT_MAX;
	return blk_validate_limits(lim);
}

@@ -708,6 +717,13 @@ int blk_stack_limits(struct queue_limits *t, struct queue_limits *b,
	t->max_dev_sectors = min_not_zero(t->max_dev_sectors, b->max_dev_sectors);
	t->max_write_zeroes_sectors = min(t->max_write_zeroes_sectors,
					b->max_write_zeroes_sectors);
	t->max_user_wzeroes_unmap_sectors =
			min(t->max_user_wzeroes_unmap_sectors,
			    b->max_user_wzeroes_unmap_sectors);
	t->max_hw_wzeroes_unmap_sectors =
			min(t->max_hw_wzeroes_unmap_sectors,
			    b->max_hw_wzeroes_unmap_sectors);

	t->max_hw_zone_append_sectors = min(t->max_hw_zone_append_sectors,
					b->max_hw_zone_append_sectors);

+26 −0
Original line number Diff line number Diff line
@@ -161,6 +161,8 @@ static ssize_t queue_##_field##_show(struct gendisk *disk, char *page) \
QUEUE_SYSFS_LIMIT_SHOW_SECTORS_TO_BYTES(max_discard_sectors)
QUEUE_SYSFS_LIMIT_SHOW_SECTORS_TO_BYTES(max_hw_discard_sectors)
QUEUE_SYSFS_LIMIT_SHOW_SECTORS_TO_BYTES(max_write_zeroes_sectors)
QUEUE_SYSFS_LIMIT_SHOW_SECTORS_TO_BYTES(max_hw_wzeroes_unmap_sectors)
QUEUE_SYSFS_LIMIT_SHOW_SECTORS_TO_BYTES(max_wzeroes_unmap_sectors)
QUEUE_SYSFS_LIMIT_SHOW_SECTORS_TO_BYTES(atomic_write_max_sectors)
QUEUE_SYSFS_LIMIT_SHOW_SECTORS_TO_BYTES(atomic_write_boundary_sectors)
QUEUE_SYSFS_LIMIT_SHOW_SECTORS_TO_BYTES(max_zone_append_sectors)
@@ -205,6 +207,24 @@ static int queue_max_discard_sectors_store(struct gendisk *disk,
	return 0;
}

static int queue_max_wzeroes_unmap_sectors_store(struct gendisk *disk,
		const char *page, size_t count, struct queue_limits *lim)
{
	unsigned long max_zeroes_bytes, max_hw_zeroes_bytes;
	ssize_t ret;

	ret = queue_var_store(&max_zeroes_bytes, page, count);
	if (ret < 0)
		return ret;

	max_hw_zeroes_bytes = lim->max_hw_wzeroes_unmap_sectors << SECTOR_SHIFT;
	if (max_zeroes_bytes != 0 && max_zeroes_bytes != max_hw_zeroes_bytes)
		return -EINVAL;

	lim->max_user_wzeroes_unmap_sectors = max_zeroes_bytes >> SECTOR_SHIFT;
	return 0;
}

static int
queue_max_sectors_store(struct gendisk *disk, const char *page, size_t count,
		struct queue_limits *lim)
@@ -514,6 +534,10 @@ QUEUE_LIM_RO_ENTRY(queue_atomic_write_unit_min, "atomic_write_unit_min_bytes");

QUEUE_RO_ENTRY(queue_write_same_max, "write_same_max_bytes");
QUEUE_LIM_RO_ENTRY(queue_max_write_zeroes_sectors, "write_zeroes_max_bytes");
QUEUE_LIM_RO_ENTRY(queue_max_hw_wzeroes_unmap_sectors,
		"write_zeroes_unmap_max_hw_bytes");
QUEUE_LIM_RW_ENTRY(queue_max_wzeroes_unmap_sectors,
		"write_zeroes_unmap_max_bytes");
QUEUE_LIM_RO_ENTRY(queue_max_zone_append_sectors, "zone_append_max_bytes");
QUEUE_LIM_RO_ENTRY(queue_zone_write_granularity, "zone_write_granularity");

@@ -662,6 +686,8 @@ static struct attribute *queue_attrs[] = {
	&queue_atomic_write_unit_min_entry.attr,
	&queue_atomic_write_unit_max_entry.attr,
	&queue_max_write_zeroes_sectors_entry.attr,
	&queue_max_hw_wzeroes_unmap_sectors_entry.attr,
	&queue_max_wzeroes_unmap_sectors_entry.attr,
	&queue_max_zone_append_sectors_entry.attr,
	&queue_zone_write_granularity_entry.attr,
	&queue_rotational_entry.attr,
+25 −19
Original line number Diff line number Diff line
@@ -844,7 +844,7 @@ static ssize_t blkdev_read_iter(struct kiocb *iocb, struct iov_iter *to)

#define	BLKDEV_FALLOC_FL_SUPPORTED					\
		(FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE |		\
		 FALLOC_FL_ZERO_RANGE)
		 FALLOC_FL_ZERO_RANGE | FALLOC_FL_WRITE_ZEROES)

static long blkdev_fallocate(struct file *file, int mode, loff_t start,
			     loff_t len)
@@ -853,11 +853,19 @@ static long blkdev_fallocate(struct file *file, int mode, loff_t start,
	struct block_device *bdev = I_BDEV(inode);
	loff_t end = start + len - 1;
	loff_t isize;
	unsigned int flags;
	int error;

	/* Fail if we don't recognize the flags. */
	if (mode & ~BLKDEV_FALLOC_FL_SUPPORTED)
		return -EOPNOTSUPP;
	/*
	 * Don't allow writing zeroes if the device does not enable the
	 * unmap write zeroes operation.
	 */
	if ((mode & FALLOC_FL_WRITE_ZEROES) &&
	    !bdev_write_zeroes_unmap_sectors(bdev))
		return -EOPNOTSUPP;

	/* Don't go off the end of the device. */
	isize = bdev_nr_bytes(bdev);
@@ -880,34 +888,32 @@ static long blkdev_fallocate(struct file *file, int mode, loff_t start,
	inode_lock(inode);
	filemap_invalidate_lock(inode->i_mapping);

	/*
	 * Invalidate the page cache, including dirty pages, for valid
	 * de-allocate mode calls to fallocate().
	 */
	switch (mode) {
	case FALLOC_FL_ZERO_RANGE:
	case FALLOC_FL_ZERO_RANGE | FALLOC_FL_KEEP_SIZE:
		error = truncate_bdev_range(bdev, file_to_blk_mode(file), start, end);
		if (error)
			goto fail;

		error = blkdev_issue_zeroout(bdev, start >> SECTOR_SHIFT,
					     len >> SECTOR_SHIFT, GFP_KERNEL,
					     BLKDEV_ZERO_NOUNMAP);
		flags = BLKDEV_ZERO_NOUNMAP;
		break;
	case FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE:
		error = truncate_bdev_range(bdev, file_to_blk_mode(file), start, end);
		if (error)
			goto fail;

		error = blkdev_issue_zeroout(bdev, start >> SECTOR_SHIFT,
					     len >> SECTOR_SHIFT, GFP_KERNEL,
					     BLKDEV_ZERO_NOFALLBACK);
		flags = BLKDEV_ZERO_NOFALLBACK;
		break;
	case FALLOC_FL_WRITE_ZEROES:
		flags = 0;
		break;
	default:
		error = -EOPNOTSUPP;
		goto fail;
	}

	/*
	 * Invalidate the page cache, including dirty pages, for valid
	 * de-allocate mode calls to fallocate().
	 */
	error = truncate_bdev_range(bdev, file_to_blk_mode(file), start, end);
	if (error)
		goto fail;

	error = blkdev_issue_zeroout(bdev, start >> SECTOR_SHIFT,
				     len >> SECTOR_SHIFT, GFP_KERNEL, flags);
 fail:
	filemap_invalidate_unlock(inode->i_mapping);
	inode_unlock(inode);
+3 −1
Original line number Diff line number Diff line
@@ -2065,8 +2065,10 @@ int dm_table_set_restrictions(struct dm_table *t, struct request_queue *q,
		limits->discard_alignment = 0;
	}

	if (!dm_table_supports_write_zeroes(t))
	if (!dm_table_supports_write_zeroes(t)) {
		limits->max_write_zeroes_sectors = 0;
		limits->max_hw_wzeroes_unmap_sectors = 0;
	}

	if (!dm_table_supports_secure_erase(t))
		limits->max_secure_erase_sectors = 0;
Loading