[PATCH 1/2] btrfs-progs: zoned: refresh stale cached zone state in btrfs_reset_dev_zone()

Johannes Thumshirn <[email protected]>
Newsgroups org.kernel.vger.linux-btrfs
Message-ID <[email protected]>
btrfs-progs reads the device zone layout once, in btrfs_get_zone_info() ->
report_zones() when the device is opened, and caches it in 'zinfo->zones[]'
without ever refreshing it afterwards. btrfs_reset_dev_zone() skips
resetting a zone whose cached condition is EMPTY, but that condition can be
stale: a zone written after open, for example a temporary chunk that mkfs
writes and then removes, can still be cached as EMPTY. Its reset is then
skipped and the zone is left open on the device, referenced by no chunk nor
dev extent, permanently consuming one of the device's limited active zones.
On a device with few active zones this results in premature ENOSPC or an
allocation hang.

Before skipping the reset of a supposedly empty zone, re-read its real
condition from the device and only skip if it is really empty.

Fixes: bfdb3ae23713 ("btrfs-progs: zoned: reset zone of freed block group")
Reported-by: Shin'ichiro Kawasaki <[email protected]>
Signed-off-by: Johannes Thumshirn <[email protected]>
---
 kernel-shared/zoned.c | 18 +++++++++++++++---
 1 file changed, 15 insertions(+), 3 deletions(-)

diff --git a/kernel-shared/zoned.c b/kernel-shared/zoned.c
index 3a4c0e9c..c891a983 100644
--- a/kernel-shared/zoned.c
+++ b/kernel-shared/zoned.c
@@ -276,11 +276,23 @@ int btrfs_reset_dev_zone(int fd, struct blk_zone *zone)
 {
 	struct blk_zone_range range;
 
-	/* Nothing to do if it is already empty */
-	if (zone->type == BLK_ZONE_TYPE_CONVENTIONAL ||
-	    zone->cond == BLK_ZONE_COND_EMPTY)
+	if (zone->type == BLK_ZONE_TYPE_CONVENTIONAL)
 		return 0;
 
+	if (zone->cond == BLK_ZONE_COND_EMPTY) {
+		char buf[sizeof(struct blk_zone_report) +
+			 sizeof(struct blk_zone)] = { 0 };
+		struct blk_zone_report *rep = (struct blk_zone_report *)buf;
+
+		rep->sector = zone->start;
+		rep->nr_zones = 1;
+		if (ioctl(fd, BLKREPORTZONE, rep) == 0 && rep->nr_zones == 1)
+			*zone = *(struct blk_zone *)(rep + 1);
+
+		if (zone->cond == BLK_ZONE_COND_EMPTY)
+			return 0;
+	}
+
 	range.sector = zone->start;
 	range.nr_sectors = zone->len;
 
-- 
2.55.0
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.