git: 1f4366669ce1 - stable/12 - loader: setting vdev size based on label asize is not working

From: Kyle Evans <kevans_at_FreeBSD.org>
Date: Fri, 08 Oct 2021 06:10:58 UTC
The branch stable/12 has been updated by kevans:

URL: https://cgit.FreeBSD.org/src/commit/?id=1f4366669ce1e6e9a750a90ff35a639e770a7521

commit 1f4366669ce1e6e9a750a90ff35a639e770a7521
Author:     Toomas Soome <tsoome@FreeBSD.org>
AuthorDate: 2020-07-09 13:19:00 +0000
Commit:     Kyle Evans <kevans@FreeBSD.org>
CommitDate: 2021-10-08 05:24:26 +0000

    loader: setting vdev size based on label asize is not working
    
    Because we need to read asize from vdev_tree. We also need to consider
    different vdev type difference.
    
    (cherry picked from commit 1a4b982e1e22dc0bbcddbff082ee16abb2874871)
---
 stand/libsa/zfs/zfsimpl.c | 58 +++++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 54 insertions(+), 4 deletions(-)

diff --git a/stand/libsa/zfs/zfsimpl.c b/stand/libsa/zfs/zfsimpl.c
index 8f299a0a3baf..b8b06fd862d1 100644
--- a/stand/libsa/zfs/zfsimpl.c
+++ b/stand/libsa/zfs/zfsimpl.c
@@ -1582,6 +1582,57 @@ vdev_label_read(vdev_t *vd, int l, void *buf, uint64_t offset,
 	return (vdev_read_phys(vd, &bp, buf, off, size));
 }
 
+static uint64_t
+vdev_get_label_asize(nvlist_t *nvl)
+{
+	nvlist_t *vdevs;
+	uint64_t asize;
+	const char *type;
+	int len;
+
+	asize = 0;
+	/* Get vdev tree */
+	if (nvlist_find(nvl, ZPOOL_CONFIG_VDEV_TREE, DATA_TYPE_NVLIST,
+	    NULL, &vdevs, NULL) != 0)
+		return (asize);
+
+	/*
+	 * Get vdev type. We will calculate asize for raidz, mirror and disk.
+	 * For raidz, the asize is raw size of all children.
+	 */
+	if (nvlist_find(vdevs, ZPOOL_CONFIG_TYPE, DATA_TYPE_STRING,
+	    NULL, &type, &len) != 0)
+		goto done;
+
+	if (memcmp(type, VDEV_TYPE_MIRROR, len) != 0 &&
+	    memcmp(type, VDEV_TYPE_DISK, len) != 0 &&
+	    memcmp(type, VDEV_TYPE_RAIDZ, len) != 0)
+		goto done;
+
+	if (nvlist_find(vdevs, ZPOOL_CONFIG_ASIZE, DATA_TYPE_UINT64,
+	    NULL, &asize, NULL) != 0)
+		goto done;
+
+	if (memcmp(type, VDEV_TYPE_RAIDZ, len) == 0) {
+		nvlist_t *kids;
+		int nkids;
+
+		if (nvlist_find(vdevs, ZPOOL_CONFIG_CHILDREN,
+		    DATA_TYPE_NVLIST_ARRAY, &nkids, &kids, NULL) != 0) {
+			asize = 0;
+			goto done;
+		}
+
+		asize /= nkids;
+		nvlist_destroy(kids);
+	}
+
+	asize += VDEV_LABEL_START_SIZE + VDEV_LABEL_END_SIZE;
+done:
+	nvlist_destroy(vdevs);
+	return (asize);
+}
+
 static nvlist_t *
 vdev_label_read_config(vdev_t *vd, uint64_t txg)
 {
@@ -1627,10 +1678,9 @@ vdev_label_read_config(vdev_t *vd, uint64_t txg)
 			 * Use asize from pool config. We need this
 			 * because we can get bad value from BIOS.
 			 */
-			if (nvlist_find(nvl, ZPOOL_CONFIG_ASIZE,
-			    DATA_TYPE_UINT64, NULL, &asize, NULL) == 0) {
-				vd->v_psize = asize +
-				    VDEV_LABEL_START_SIZE + VDEV_LABEL_END_SIZE;
+			asize = vdev_get_label_asize(nvl);
+			if (asize != 0) {
+				vd->v_psize = asize;
 			}
 		}
 		nvlist_destroy(tmp);