svn commit: r192194 - in head/sys: boot/i386/zfsboot boot/zfs cddl/boot/zfs

Doug Rabson dfr at FreeBSD.org
Sat May 16 10:48:22 UTC 2009


Author: dfr
Date: Sat May 16 10:48:20 2009
New Revision: 192194
URL: http://svn.freebsd.org/changeset/base/192194

Log:
  Add support for booting from raidz1 and raidz2 pools.

Modified:
  head/sys/boot/i386/zfsboot/zfsboot.c
  head/sys/boot/zfs/zfsimpl.c
  head/sys/cddl/boot/zfs/README
  head/sys/cddl/boot/zfs/zfsimpl.h
  head/sys/cddl/boot/zfs/zfssubr.c

Modified: head/sys/boot/i386/zfsboot/zfsboot.c
==============================================================================
--- head/sys/boot/i386/zfsboot/zfsboot.c	Sat May 16 10:42:00 2009	(r192193)
+++ head/sys/boot/i386/zfsboot/zfsboot.c	Sat May 16 10:48:20 2009	(r192194)
@@ -413,6 +413,20 @@ int13probe(int drive)
     return(0);
 }
 
+/*
+ * We call this when we find a ZFS vdev - ZFS consumes the dsk
+ * structure so we must make a new one.
+ */
+static struct dsk *
+copy_dsk(struct dsk *dsk)
+{
+    struct dsk *newdsk;
+
+    newdsk = malloc(sizeof(struct dsk));
+    *newdsk = *dsk;
+    return (newdsk);
+}
+
 static void
 probe_drive(struct dsk *dsk, spa_t **spap)
 {
@@ -426,9 +440,6 @@ probe_drive(struct dsk *dsk, spa_t **spa
     char *sec;
     unsigned i;
 
-    if (!int13probe(dsk->drive))
-	return;
-
     /*
      * If we find a vdev on the whole disk, stop here. Otherwise dig
      * out the MBR and probe each slice in turn for a vdev.
@@ -473,7 +484,7 @@ probe_drive(struct dsk *dsk, spa_t **spa
 		if (vdev_probe(vdev_read, dsk, spap) == 0) {
 		    /*
 		     * We record the first pool we find (we will try
-		     * to boot from that one.
+		     * to boot from that one).
 		     */
 		    spap = 0;
 
@@ -481,10 +492,7 @@ probe_drive(struct dsk *dsk, spa_t **spa
 		     * This slice had a vdev. We need a new dsk
 		     * structure now since the vdev now owns this one.
 		     */
-		    struct dsk *newdsk;
-		    newdsk = malloc(sizeof(struct dsk));
-		    *newdsk = *dsk;
-		    dsk = newdsk;
+		    dsk = copy_dsk(dsk);
 		}
 		break;
 	    }
@@ -514,10 +522,7 @@ trymbr:
 	     * This slice had a vdev. We need a new dsk structure now
 	     * since the vdev now owns this one.
 	     */
-	    struct dsk *newdsk;
-	    newdsk = malloc(sizeof(struct dsk));
-	    *newdsk = *dsk;
-	    dsk = newdsk;
+	    dsk = copy_dsk(dsk);
 	}
     }
 }
@@ -569,10 +574,13 @@ main(void)
      * will find any other available pools and it may fill in missing
      * vdevs for the boot pool.
      */
-    for (i = 0; i < 4; i++) {
+    for (i = 0; i < 128; i++) {
 	if ((i | DRV_HARD) == *(uint8_t *)PTOV(ARGS))
 	    continue;
 
+	if (!int13probe(i | DRV_HARD))
+	    break;
+
 	dsk = malloc(sizeof(struct dsk));
 	dsk->drive = i | DRV_HARD;
 	dsk->type = dsk->drive & TYPE_AD;
@@ -944,7 +952,7 @@ static int
 drvread(struct dsk *dsk, void *buf, unsigned lba, unsigned nblk)
 {
 #ifdef GPT
-   static unsigned c = 0x2d5c7c2f;
+    static unsigned c = 0x2d5c7c2f;
 
     if (!OPT_CHECK(RBX_QUIET))
 	printf("%c\b", c = c << 8 | c >> 24);

Modified: head/sys/boot/zfs/zfsimpl.c
==============================================================================
--- head/sys/boot/zfs/zfsimpl.c	Sat May 16 10:42:00 2009	(r192193)
+++ head/sys/boot/zfs/zfsimpl.c	Sat May 16 10:48:20 2009	(r192194)
@@ -45,16 +45,13 @@ static vdev_list_t zfs_vdevs;
 static spa_list_t zfs_pools;
 
 static uint64_t zfs_crc64_table[256];
-static char *zfs_decomp_buf;
 static const dnode_phys_t *dnode_cache_obj = 0;
 static uint64_t dnode_cache_bn;
 static char *dnode_cache_buf;
 static char *zap_scratch;
+static char *zfs_temp_buf, *zfs_temp_end, *zfs_temp_ptr;
 
-/*
- * Forward declarations.
- */
-static int zio_read_phys(vdev_t *vdev, const blkptr_t *bp, void *buf, off_t offset);
+#define TEMP_SIZE	(1*SPA_MAXBLOCKSIZE)
 
 static void
 zfs_init(void)
@@ -62,13 +59,37 @@ zfs_init(void)
 	STAILQ_INIT(&zfs_vdevs);
 	STAILQ_INIT(&zfs_pools);
 
-	zfs_decomp_buf = malloc(128*1024);
-	dnode_cache_buf = malloc(128*1024);
-	zap_scratch = malloc(128*1024);
+	zfs_temp_buf = malloc(TEMP_SIZE);
+	zfs_temp_end = zfs_temp_buf + TEMP_SIZE;
+	zfs_temp_ptr = zfs_temp_buf;
+	dnode_cache_buf = malloc(SPA_MAXBLOCKSIZE);
+	zap_scratch = malloc(SPA_MAXBLOCKSIZE);
 
 	zfs_init_crc();
 }
 
+static char *
+zfs_alloc_temp(size_t sz)
+{
+	char *p;
+
+	if (zfs_temp_ptr + sz > zfs_temp_end) {
+		printf("ZFS: out of temporary buffer space\n");
+		for (;;) ;
+	}
+	p = zfs_temp_ptr;
+	zfs_temp_ptr += sz;
+
+	return (p);
+}
+
+static void
+zfs_reset_temp(void)
+{
+
+	zfs_temp_ptr = zfs_temp_buf;
+}
+
 static int
 xdr_int(const unsigned char **xdr, int *ip)
 {
@@ -299,7 +320,41 @@ nvlist_print(const unsigned char *nvlist
 #endif
 
 static int
-vdev_mirror_read(vdev_t *vdev, void *priv, off_t offset, void *buf, size_t size)
+vdev_read_phys(vdev_t *vdev, const blkptr_t *bp, void *buf,
+    off_t offset, size_t size)
+{
+	size_t psize;
+	int rc;
+
+	if (bp) {
+		psize = BP_GET_PSIZE(bp);
+	} else {
+		psize = size;
+	}
+
+	/*printf("ZFS: reading %d bytes at 0x%llx to %p\n", psize, offset, buf);*/
+	rc = vdev->v_phys_read(vdev, vdev->v_read_priv, offset, buf, psize);
+	if (rc)
+		return (rc);
+	if (bp && zio_checksum_error(bp, buf))
+		return (EIO);
+
+	return (0);
+}
+
+static int
+vdev_disk_read(vdev_t *vdev, const blkptr_t *bp, void *buf,
+    off_t offset, size_t bytes)
+{
+
+	return (vdev_read_phys(vdev, bp, buf,
+		offset + VDEV_LABEL_START_SIZE, bytes));
+}
+
+
+static int
+vdev_mirror_read(vdev_t *vdev, const blkptr_t *bp, void *buf,
+    off_t offset, size_t bytes)
 {
 	vdev_t *kid;
 	int rc;
@@ -308,7 +363,7 @@ vdev_mirror_read(vdev_t *vdev, void *pri
 	STAILQ_FOREACH(kid, &vdev->v_children, v_childlink) {
 		if (kid->v_state != VDEV_STATE_HEALTHY)
 			continue;
-		rc = kid->v_read(kid, kid->v_read_priv, offset, buf, size);
+		rc = kid->v_read(kid, bp, buf, offset, bytes);
 		if (!rc)
 			return (0);
 	}
@@ -329,7 +384,7 @@ vdev_find(uint64_t guid)
 }
 
 static vdev_t *
-vdev_create(uint64_t guid, vdev_read_t *read, void *read_priv)
+vdev_create(uint64_t guid, vdev_read_t *read)
 {
 	vdev_t *vdev;
 
@@ -339,7 +394,8 @@ vdev_create(uint64_t guid, vdev_read_t *
 	vdev->v_guid = guid;
 	vdev->v_state = VDEV_STATE_OFFLINE;
 	vdev->v_read = read;
-	vdev->v_read_priv = read_priv;
+	vdev->v_phys_read = 0;
+	vdev->v_read_priv = 0;
 	STAILQ_INSERT_TAIL(&zfs_vdevs, vdev, v_alllink);
 
 	return (vdev);
@@ -349,7 +405,7 @@ static int
 vdev_init_from_nvlist(const unsigned char *nvlist, vdev_t **vdevp)
 {
 	int rc;
-	uint64_t guid, id;
+	uint64_t guid, id, ashift, nparity;
 	const char *type;
 	const char *path;
 	vdev_t *vdev, *kid;
@@ -378,17 +434,30 @@ vdev_init_from_nvlist(const unsigned cha
 	}
 
 	if (strcmp(type, VDEV_TYPE_MIRROR)
-	    && strcmp(type, VDEV_TYPE_DISK)) {
-		printf("ZFS: can only boot from disk or mirror vdevs\n");
+	    && strcmp(type, VDEV_TYPE_DISK)
+	    && strcmp(type, VDEV_TYPE_RAIDZ)) {
+		printf("ZFS: can only boot from disk, mirror or raidz vdevs\n");
 		return (EIO);
 	}
 
 	if (!strcmp(type, VDEV_TYPE_MIRROR))
-		vdev = vdev_create(guid, vdev_mirror_read, 0);
+		vdev = vdev_create(guid, vdev_mirror_read);
+	else if (!strcmp(type, VDEV_TYPE_RAIDZ))
+		vdev = vdev_create(guid, vdev_raidz_read);
 	else
-		vdev = vdev_create(guid, 0, 0);
-
+		vdev = vdev_create(guid, vdev_disk_read);
 
+	vdev->v_id = id;
+	if (nvlist_find(nvlist, ZPOOL_CONFIG_ASHIFT,
+		DATA_TYPE_UINT64, 0, &ashift) == 0)
+		vdev->v_ashift = ashift;
+	else
+		vdev->v_ashift = 0;
+	if (nvlist_find(nvlist, ZPOOL_CONFIG_NPARITY,
+		DATA_TYPE_UINT64, 0, &nparity) == 0)
+		vdev->v_nparity = nparity;
+	else
+		vdev->v_nparity = 0;
 	if (nvlist_find(nvlist, ZPOOL_CONFIG_PATH,
 			DATA_TYPE_STRING, 0, &path) == 0) {
 		if (strlen(path) > 5
@@ -400,15 +469,22 @@ vdev_init_from_nvlist(const unsigned cha
 			path += 5;
 		vdev->v_name = strdup(path);
 	} else {
-		vdev->v_name = strdup(type);
+		if (!strcmp(type, "raidz")) {
+			if (vdev->v_nparity == 1)
+				vdev->v_name = "raidz1";
+			else
+				vdev->v_name = "raidz2";
+		} else {
+			vdev->v_name = strdup(type);
+		}
 	}
-	vdev->v_id = id;
 	rc = nvlist_find(nvlist, ZPOOL_CONFIG_CHILDREN,
 			 DATA_TYPE_NVLIST_ARRAY, &nkids, &kids);
 	/*
 	 * Its ok if we don't have any kids.
 	 */
 	if (rc == 0) {
+		vdev->v_nchildren = nkids;
 		for (i = 0; i < nkids; i++) {
 			rc = vdev_init_from_nvlist(kids, &kid);
 			if (rc)
@@ -416,6 +492,8 @@ vdev_init_from_nvlist(const unsigned cha
 			STAILQ_INSERT_TAIL(&vdev->v_children, kid, v_childlink);
 			kids = nvlist_next(kids);
 		}
+	} else {
+		vdev->v_nchildren = 0;
 	}
 
 	if (vdevp)
@@ -431,11 +509,10 @@ vdev_set_state(vdev_t *vdev)
 	int bad_kids;
 
 	/*
-	 * We assume that if we have kids, we are a mirror. A mirror
-	 * is healthy if all its kids are healthy. Its degraded (but
-	 * working) if at least one kid is healty.
+	 * A mirror or raidz is healthy if all its kids are healthy. A
+	 * mirror is degraded if any of its kids is healthy; a raidz
+	 * is degraded if at most nparity kids are offline.
 	 */
-
 	if (STAILQ_FIRST(&vdev->v_children)) {
 		good_kids = 0;
 		bad_kids = 0;
@@ -445,13 +522,22 @@ vdev_set_state(vdev_t *vdev)
 			else
 				bad_kids++;
 		}
-		if (good_kids) {
-			if (!bad_kids && good_kids)
-				vdev->v_state = VDEV_STATE_HEALTHY;
-			else
-				vdev->v_state = VDEV_STATE_DEGRADED;
+		if (bad_kids == 0) {
+			vdev->v_state = VDEV_STATE_HEALTHY;
 		} else {
-			vdev->v_state = VDEV_STATE_OFFLINE;
+			if (vdev->v_read == vdev_mirror_read) {
+				if (good_kids) {
+					vdev->v_state = VDEV_STATE_DEGRADED;
+				} else {
+					vdev->v_state = VDEV_STATE_OFFLINE;
+				}
+			} else if (vdev->v_read == vdev_raidz_read) {
+				if (bad_kids > vdev->v_nparity) {
+					vdev->v_state = VDEV_STATE_OFFLINE;
+				} else {
+					vdev->v_state = VDEV_STATE_DEGRADED;
+				}
+			}
 		}
 	}
 }
@@ -609,7 +695,7 @@ spa_all_status(void)
 }
 
 static int
-vdev_probe(vdev_read_t *read, void *read_priv, spa_t **spap)
+vdev_probe(vdev_phys_read_t *read, void *read_priv, spa_t **spap)
 {
 	vdev_t vtmp;
 	vdev_phys_t *vdev_label = (vdev_phys_t *) zap_scratch;
@@ -632,7 +718,7 @@ vdev_probe(vdev_read_t *read, void *read
 	 * uberblock is most current.
 	 */
 	memset(&vtmp, 0, sizeof(vtmp));
-	vtmp.v_read = read;
+	vtmp.v_phys_read = read;
 	vtmp.v_read_priv = read_priv;
 	off = offsetof(vdev_label_t, vl_vdev_phys);
 	BP_ZERO(&bp);
@@ -641,7 +727,7 @@ vdev_probe(vdev_read_t *read, void *read
 	BP_SET_CHECKSUM(&bp, ZIO_CHECKSUM_LABEL);
 	BP_SET_COMPRESS(&bp, ZIO_COMPRESS_OFF);
 	ZIO_SET_CHECKSUM(&bp.blk_cksum, off, 0, 0, 0);
-	if (zio_read_phys(&vtmp, &bp, vdev_label, off))
+	if (vdev_read_phys(&vtmp, &bp, vdev_label, off, 0))
 		return (EIO);
 
 	if (vdev_label->vp_nvlist[0] != NV_ENCODE_XDR) {
@@ -668,6 +754,7 @@ vdev_probe(vdev_read_t *read, void *read
 		return (EIO);
 	}
 
+#ifndef TEST
 	if (val != POOL_STATE_ACTIVE) {
 		/*
 		 * Don't print a message here. If we happen to reboot
@@ -677,6 +764,7 @@ vdev_probe(vdev_read_t *read, void *read
 		/*printf("ZFS: pool is not active\n");*/
 		return (EIO);
 	}
+#endif
 
 	if (nvlist_find(nvlist,
 			ZPOOL_CONFIG_POOL_TXG,
@@ -687,7 +775,11 @@ vdev_probe(vdev_read_t *read, void *read
 	    || nvlist_find(nvlist,
 			   ZPOOL_CONFIG_POOL_NAME,
 			   DATA_TYPE_STRING, 0, &pool_name)) {
-		printf("ZFS: can't find pool details\n");
+		/*
+		 * Cache and spare devices end up here - just ignore
+		 * them.
+		 */
+		/*printf("ZFS: can't find pool details\n");*/
 		return (EIO);
 	}
 
@@ -742,7 +834,7 @@ vdev_probe(vdev_read_t *read, void *read
 	 */
 	vdev = vdev_find(guid);
 	if (vdev) {
-		vdev->v_read = read;
+		vdev->v_phys_read = read;
 		vdev->v_read_priv = read_priv;
 		vdev->v_state = VDEV_STATE_HEALTHY;
 	} else {
@@ -772,7 +864,7 @@ vdev_probe(vdev_read_t *read, void *read
 		BP_SET_CHECKSUM(&bp, ZIO_CHECKSUM_LABEL);
 		BP_SET_COMPRESS(&bp, ZIO_COMPRESS_OFF);
 		ZIO_SET_CHECKSUM(&bp.blk_cksum, off, 0, 0, 0);
-		if (zio_read_phys(vdev, &bp, upbuf, off))
+		if (vdev_read_phys(vdev, &bp, upbuf, off, 0))
 			continue;
 
 		up = (const struct uberblock *) upbuf;
@@ -805,39 +897,20 @@ ilog2(int n)
 }
 
 static int
-zio_read_phys(vdev_t *vdev, const blkptr_t *bp, void *buf, off_t offset)
+zio_read(spa_t *spa, const blkptr_t *bp, void *buf)
 {
 	int cpfunc = BP_GET_COMPRESS(bp);
 	size_t lsize = BP_GET_LSIZE(bp);
 	size_t psize = BP_GET_PSIZE(bp);
-	int rc;
-
-	/*printf("ZFS: reading %d bytes at 0x%llx to %p\n", psize, offset, buf);*/
-	if (cpfunc != ZIO_COMPRESS_OFF) {
-		rc = vdev->v_read(vdev, vdev->v_read_priv, offset, zfs_decomp_buf, psize);
-		if (rc)
-			return (rc);
-		if (zio_checksum_error(bp, zfs_decomp_buf))
-			return (EIO);
-		if (zio_decompress_data(cpfunc, zfs_decomp_buf, psize,
-			buf, lsize))
-			return (EIO);
-	} else {
-		rc = vdev->v_read(vdev, vdev->v_read_priv, offset, buf, psize);
-		if (rc)
-			return (rc);
-					  
-		if (zio_checksum_error(bp, buf))
-			return (EIO);
-	}
-	return (0);
-}
-
-static int
-zio_read(spa_t *spa, const blkptr_t *bp, void *buf)
-{
+	void *pbuf;
 	int i;
 
+	zfs_reset_temp();
+	if (cpfunc != ZIO_COMPRESS_OFF)
+		pbuf = zfs_alloc_temp(psize);
+	else
+		pbuf = buf;
+
 	for (i = 0; i < SPA_DVAS_PER_BP; i++) {
 		const dva_t *dva = &bp->blk_dva[i];
 		vdev_t *vdev;
@@ -848,15 +921,21 @@ zio_read(spa_t *spa, const blkptr_t *bp,
 			continue;
 
 		vdevid = DVA_GET_VDEV(dva);
-		offset = DVA_GET_OFFSET(dva) + VDEV_LABEL_START_SIZE;
+		offset = DVA_GET_OFFSET(dva);
 		STAILQ_FOREACH(vdev, &spa->spa_vdevs, v_childlink)
 			if (vdev->v_id == vdevid)
 				break;
 		if (!vdev || !vdev->v_read)
 			continue;
-		if (zio_read_phys(vdev, bp, buf, offset))
+		if (vdev->v_read(vdev, bp, pbuf, offset, psize))
 			continue;
 
+		if (cpfunc != ZIO_COMPRESS_OFF) {
+			if (zio_decompress_data(cpfunc, pbuf, psize,
+				buf, lsize))
+				return (EIO);
+		}
+
 		return (0);
 	}
 	printf("ZFS: i/o error - all block copies unavailable\n");

Modified: head/sys/cddl/boot/zfs/README
==============================================================================
--- head/sys/cddl/boot/zfs/README	Sat May 16 10:42:00 2009	(r192193)
+++ head/sys/cddl/boot/zfs/README	Sat May 16 10:48:20 2009	(r192194)
@@ -6,7 +6,7 @@ are used by the ZFS bootstrap:
     fletcher.c			checksum support
     sha256.c			checksum support
     lzjb.c			compression support
-    zfssubr.c			mostly checksum and compression support
+    zfssubr.c			checksum, compression and raidz support
     zfsimpl.h			mostly describing the physical layout
 
 The files fletcher.c, lzjb.c and sha256.c are largely identical to the

Modified: head/sys/cddl/boot/zfs/zfsimpl.h
==============================================================================
--- head/sys/cddl/boot/zfs/zfsimpl.h	Sat May 16 10:42:00 2009	(r192193)
+++ head/sys/cddl/boot/zfs/zfsimpl.h	Sat May 16 10:48:20 2009	(r192194)
@@ -1137,7 +1137,10 @@ typedef struct znode_phys {
  * In-core vdev representation.
  */
 struct vdev;
-typedef int vdev_read_t(struct vdev *vdev, void *priv, off_t offset, void *buf, size_t bytes);
+typedef int vdev_phys_read_t(struct vdev *vdev, void *priv,
+    off_t offset, void *buf, size_t bytes);
+typedef int vdev_read_t(struct vdev *vdev, const blkptr_t *bp,
+    void *buf, off_t offset, size_t bytes);
 
 typedef STAILQ_HEAD(vdev_list, vdev) vdev_list_t;
 
@@ -1148,8 +1151,12 @@ typedef struct vdev {
 	char		*v_name;	/* vdev name */
 	uint64_t	v_guid;		/* vdev guid */
 	int		v_id;		/* index in parent */
+	int		v_ashift;	/* offset to block shift */
+	int		v_nparity;	/* # parity for raidz */
+	int		v_nchildren;	/* # children */
 	vdev_state_t	v_state;	/* current state */
-	vdev_read_t	*v_read;	/* function to read from this vdev */
+	vdev_phys_read_t *v_phys_read;	/* read from raw leaf vdev */
+	vdev_read_t	*v_read;	/* read from vdev */
 	void		*v_read_priv;	/* private data for read function */
 } vdev_t;
 

Modified: head/sys/cddl/boot/zfs/zfssubr.c
==============================================================================
--- head/sys/cddl/boot/zfs/zfssubr.c	Sat May 16 10:42:00 2009	(r192193)
+++ head/sys/cddl/boot/zfs/zfssubr.c	Sat May 16 10:48:20 2009	(r192194)
@@ -191,3 +191,735 @@ zap_hash(uint64_t salt, const char *name
 
 	return (crc);
 }
+
+static char *zfs_alloc_temp(size_t sz);
+
+typedef struct raidz_col {
+	uint64_t rc_devidx;		/* child device index for I/O */
+	uint64_t rc_offset;		/* device offset */
+	uint64_t rc_size;		/* I/O size */
+	void *rc_data;			/* I/O data */
+	int rc_error;			/* I/O error for this device */
+	uint8_t rc_tried;		/* Did we attempt this I/O column? */
+	uint8_t rc_skipped;		/* Did we skip this I/O column? */
+} raidz_col_t;
+
+#define	VDEV_RAIDZ_P		0
+#define	VDEV_RAIDZ_Q		1
+
+static void
+vdev_raidz_reconstruct_p(raidz_col_t *cols, int nparity, int acols, int x)
+{
+	uint64_t *dst, *src, xcount, ccount, count, i;
+	int c;
+
+	xcount = cols[x].rc_size / sizeof (src[0]);
+	//ASSERT(xcount <= cols[VDEV_RAIDZ_P].rc_size / sizeof (src[0]));
+	//ASSERT(xcount > 0);
+
+	src = cols[VDEV_RAIDZ_P].rc_data;
+	dst = cols[x].rc_data;
+	for (i = 0; i < xcount; i++, dst++, src++) {
+		*dst = *src;
+	}
+
+	for (c = nparity; c < acols; c++) {
+		src = cols[c].rc_data;
+		dst = cols[x].rc_data;
+
+		if (c == x)
+			continue;
+
+		ccount = cols[c].rc_size / sizeof (src[0]);
+		count = MIN(ccount, xcount);
+
+		for (i = 0; i < count; i++, dst++, src++) {
+			*dst ^= *src;
+		}
+	}
+}
+
+/*
+ * These two tables represent powers and logs of 2 in the Galois field defined
+ * above. These values were computed by repeatedly multiplying by 2 as above.
+ */
+static const uint8_t vdev_raidz_pow2[256] = {
+	0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80,
+	0x1d, 0x3a, 0x74, 0xe8, 0xcd, 0x87, 0x13, 0x26,
+	0x4c, 0x98, 0x2d, 0x5a, 0xb4, 0x75, 0xea, 0xc9,
+	0x8f, 0x03, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0,
+	0x9d, 0x27, 0x4e, 0x9c, 0x25, 0x4a, 0x94, 0x35,
+	0x6a, 0xd4, 0xb5, 0x77, 0xee, 0xc1, 0x9f, 0x23,
+	0x46, 0x8c, 0x05, 0x0a, 0x14, 0x28, 0x50, 0xa0,
+	0x5d, 0xba, 0x69, 0xd2, 0xb9, 0x6f, 0xde, 0xa1,
+	0x5f, 0xbe, 0x61, 0xc2, 0x99, 0x2f, 0x5e, 0xbc,
+	0x65, 0xca, 0x89, 0x0f, 0x1e, 0x3c, 0x78, 0xf0,
+	0xfd, 0xe7, 0xd3, 0xbb, 0x6b, 0xd6, 0xb1, 0x7f,
+	0xfe, 0xe1, 0xdf, 0xa3, 0x5b, 0xb6, 0x71, 0xe2,
+	0xd9, 0xaf, 0x43, 0x86, 0x11, 0x22, 0x44, 0x88,
+	0x0d, 0x1a, 0x34, 0x68, 0xd0, 0xbd, 0x67, 0xce,
+	0x81, 0x1f, 0x3e, 0x7c, 0xf8, 0xed, 0xc7, 0x93,
+	0x3b, 0x76, 0xec, 0xc5, 0x97, 0x33, 0x66, 0xcc,
+	0x85, 0x17, 0x2e, 0x5c, 0xb8, 0x6d, 0xda, 0xa9,
+	0x4f, 0x9e, 0x21, 0x42, 0x84, 0x15, 0x2a, 0x54,
+	0xa8, 0x4d, 0x9a, 0x29, 0x52, 0xa4, 0x55, 0xaa,
+	0x49, 0x92, 0x39, 0x72, 0xe4, 0xd5, 0xb7, 0x73,
+	0xe6, 0xd1, 0xbf, 0x63, 0xc6, 0x91, 0x3f, 0x7e,
+	0xfc, 0xe5, 0xd7, 0xb3, 0x7b, 0xf6, 0xf1, 0xff,
+	0xe3, 0xdb, 0xab, 0x4b, 0x96, 0x31, 0x62, 0xc4,
+	0x95, 0x37, 0x6e, 0xdc, 0xa5, 0x57, 0xae, 0x41,
+	0x82, 0x19, 0x32, 0x64, 0xc8, 0x8d, 0x07, 0x0e,
+	0x1c, 0x38, 0x70, 0xe0, 0xdd, 0xa7, 0x53, 0xa6,
+	0x51, 0xa2, 0x59, 0xb2, 0x79, 0xf2, 0xf9, 0xef,
+	0xc3, 0x9b, 0x2b, 0x56, 0xac, 0x45, 0x8a, 0x09,
+	0x12, 0x24, 0x48, 0x90, 0x3d, 0x7a, 0xf4, 0xf5,
+	0xf7, 0xf3, 0xfb, 0xeb, 0xcb, 0x8b, 0x0b, 0x16,
+	0x2c, 0x58, 0xb0, 0x7d, 0xfa, 0xe9, 0xcf, 0x83,
+	0x1b, 0x36, 0x6c, 0xd8, 0xad, 0x47, 0x8e, 0x01
+};
+static const uint8_t vdev_raidz_log2[256] = {
+	0x00, 0x00, 0x01, 0x19, 0x02, 0x32, 0x1a, 0xc6,
+	0x03, 0xdf, 0x33, 0xee, 0x1b, 0x68, 0xc7, 0x4b,
+	0x04, 0x64, 0xe0, 0x0e, 0x34, 0x8d, 0xef, 0x81,
+	0x1c, 0xc1, 0x69, 0xf8, 0xc8, 0x08, 0x4c, 0x71,
+	0x05, 0x8a, 0x65, 0x2f, 0xe1, 0x24, 0x0f, 0x21,
+	0x35, 0x93, 0x8e, 0xda, 0xf0, 0x12, 0x82, 0x45,
+	0x1d, 0xb5, 0xc2, 0x7d, 0x6a, 0x27, 0xf9, 0xb9,
+	0xc9, 0x9a, 0x09, 0x78, 0x4d, 0xe4, 0x72, 0xa6,
+	0x06, 0xbf, 0x8b, 0x62, 0x66, 0xdd, 0x30, 0xfd,
+	0xe2, 0x98, 0x25, 0xb3, 0x10, 0x91, 0x22, 0x88,
+	0x36, 0xd0, 0x94, 0xce, 0x8f, 0x96, 0xdb, 0xbd,
+	0xf1, 0xd2, 0x13, 0x5c, 0x83, 0x38, 0x46, 0x40,
+	0x1e, 0x42, 0xb6, 0xa3, 0xc3, 0x48, 0x7e, 0x6e,
+	0x6b, 0x3a, 0x28, 0x54, 0xfa, 0x85, 0xba, 0x3d,
+	0xca, 0x5e, 0x9b, 0x9f, 0x0a, 0x15, 0x79, 0x2b,
+	0x4e, 0xd4, 0xe5, 0xac, 0x73, 0xf3, 0xa7, 0x57,
+	0x07, 0x70, 0xc0, 0xf7, 0x8c, 0x80, 0x63, 0x0d,
+	0x67, 0x4a, 0xde, 0xed, 0x31, 0xc5, 0xfe, 0x18,
+	0xe3, 0xa5, 0x99, 0x77, 0x26, 0xb8, 0xb4, 0x7c,
+	0x11, 0x44, 0x92, 0xd9, 0x23, 0x20, 0x89, 0x2e,
+	0x37, 0x3f, 0xd1, 0x5b, 0x95, 0xbc, 0xcf, 0xcd,
+	0x90, 0x87, 0x97, 0xb2, 0xdc, 0xfc, 0xbe, 0x61,
+	0xf2, 0x56, 0xd3, 0xab, 0x14, 0x2a, 0x5d, 0x9e,
+	0x84, 0x3c, 0x39, 0x53, 0x47, 0x6d, 0x41, 0xa2,
+	0x1f, 0x2d, 0x43, 0xd8, 0xb7, 0x7b, 0xa4, 0x76,
+	0xc4, 0x17, 0x49, 0xec, 0x7f, 0x0c, 0x6f, 0xf6,
+	0x6c, 0xa1, 0x3b, 0x52, 0x29, 0x9d, 0x55, 0xaa,
+	0xfb, 0x60, 0x86, 0xb1, 0xbb, 0xcc, 0x3e, 0x5a,
+	0xcb, 0x59, 0x5f, 0xb0, 0x9c, 0xa9, 0xa0, 0x51,
+	0x0b, 0xf5, 0x16, 0xeb, 0x7a, 0x75, 0x2c, 0xd7,
+	0x4f, 0xae, 0xd5, 0xe9, 0xe6, 0xe7, 0xad, 0xe8,
+	0x74, 0xd6, 0xf4, 0xea, 0xa8, 0x50, 0x58, 0xaf,
+};
+
+/*
+ * Multiply a given number by 2 raised to the given power.
+ */
+static uint8_t
+vdev_raidz_exp2(uint8_t a, int exp)
+{
+	if (a == 0)
+		return (0);
+
+	//ASSERT(exp >= 0);
+	//ASSERT(vdev_raidz_log2[a] > 0 || a == 1);
+
+	exp += vdev_raidz_log2[a];
+	if (exp > 255)
+		exp -= 255;
+
+	return (vdev_raidz_pow2[exp]);
+}
+
+static void
+vdev_raidz_generate_parity_pq(raidz_col_t *cols, int nparity, int acols)
+{
+	uint64_t *q, *p, *src, pcount, ccount, mask, i;
+	int c;
+
+	pcount = cols[VDEV_RAIDZ_P].rc_size / sizeof (src[0]);
+	//ASSERT(cols[VDEV_RAIDZ_P].rc_size == cols[VDEV_RAIDZ_Q].rc_size);
+
+	for (c = nparity; c < acols; c++) {
+		src = cols[c].rc_data;
+		p = cols[VDEV_RAIDZ_P].rc_data;
+		q = cols[VDEV_RAIDZ_Q].rc_data;
+		ccount = cols[c].rc_size / sizeof (src[0]);
+
+		if (c == nparity) {
+			//ASSERT(ccount == pcount || ccount == 0);
+			for (i = 0; i < ccount; i++, p++, q++, src++) {
+				*q = *src;
+				*p = *src;
+			}
+			for (; i < pcount; i++, p++, q++, src++) {
+				*q = 0;
+				*p = 0;
+			}
+		} else {
+			//ASSERT(ccount <= pcount);
+
+			/*
+			 * Rather than multiplying each byte
+			 * individually (as described above), we are
+			 * able to handle 8 at once by generating a
+			 * mask based on the high bit in each byte and
+			 * using that to conditionally XOR in 0x1d.
+			 */
+			for (i = 0; i < ccount; i++, p++, q++, src++) {
+				mask = *q & 0x8080808080808080ULL;
+				mask = (mask << 1) - (mask >> 7);
+				*q = ((*q << 1) & 0xfefefefefefefefeULL) ^
+				    (mask & 0x1d1d1d1d1d1d1d1dULL);
+				*q ^= *src;
+				*p ^= *src;
+			}
+
+			/*
+			 * Treat short columns as though they are full of 0s.
+			 */
+			for (; i < pcount; i++, q++) {
+				mask = *q & 0x8080808080808080ULL;
+				mask = (mask << 1) - (mask >> 7);
+				*q = ((*q << 1) & 0xfefefefefefefefeULL) ^
+				    (mask & 0x1d1d1d1d1d1d1d1dULL);
+			}
+		}
+	}
+}
+
+static void
+vdev_raidz_reconstruct_q(raidz_col_t *cols, int nparity, int acols, int x)
+{
+	uint64_t *dst, *src, xcount, ccount, count, mask, i;
+	uint8_t *b;
+	int c, j, exp;
+
+	xcount = cols[x].rc_size / sizeof (src[0]);
+	//ASSERT(xcount <= cols[VDEV_RAIDZ_Q].rc_size / sizeof (src[0]));
+
+	for (c = nparity; c < acols; c++) {
+		src = cols[c].rc_data;
+		dst = cols[x].rc_data;
+
+		if (c == x)
+			ccount = 0;
+		else
+			ccount = cols[c].rc_size / sizeof (src[0]);
+
+		count = MIN(ccount, xcount);
+
+		if (c == nparity) {
+			for (i = 0; i < count; i++, dst++, src++) {
+				*dst = *src;
+			}
+			for (; i < xcount; i++, dst++) {
+				*dst = 0;
+			}
+
+		} else {
+			/*
+			 * For an explanation of this, see the comment in
+			 * vdev_raidz_generate_parity_pq() above.
+			 */
+			for (i = 0; i < count; i++, dst++, src++) {
+				mask = *dst & 0x8080808080808080ULL;
+				mask = (mask << 1) - (mask >> 7);
+				*dst = ((*dst << 1) & 0xfefefefefefefefeULL) ^
+				    (mask & 0x1d1d1d1d1d1d1d1dULL);
+				*dst ^= *src;
+			}
+
+			for (; i < xcount; i++, dst++) {
+				mask = *dst & 0x8080808080808080ULL;
+				mask = (mask << 1) - (mask >> 7);
+				*dst = ((*dst << 1) & 0xfefefefefefefefeULL) ^
+				    (mask & 0x1d1d1d1d1d1d1d1dULL);
+			}
+		}
+	}
+
+	src = cols[VDEV_RAIDZ_Q].rc_data;
+	dst = cols[x].rc_data;
+	exp = 255 - (acols - 1 - x);
+
+	for (i = 0; i < xcount; i++, dst++, src++) {
+		*dst ^= *src;
+		for (j = 0, b = (uint8_t *)dst; j < 8; j++, b++) {
+			*b = vdev_raidz_exp2(*b, exp);
+		}
+	}
+}
+
+
+static void
+vdev_raidz_reconstruct_pq(raidz_col_t *cols, int nparity, int acols,
+    int x, int y)
+{
+	uint8_t *p, *q, *pxy, *qxy, *xd, *yd, tmp, a, b, aexp, bexp;
+	void *pdata, *qdata;
+	uint64_t xsize, ysize, i;
+
+	//ASSERT(x < y);
+	//ASSERT(x >= nparity);
+	//ASSERT(y < acols);
+
+	//ASSERT(cols[x].rc_size >= cols[y].rc_size);
+
+	/*
+	 * Move the parity data aside -- we're going to compute parity as
+	 * though columns x and y were full of zeros -- Pxy and Qxy. We want to
+	 * reuse the parity generation mechanism without trashing the actual
+	 * parity so we make those columns appear to be full of zeros by
+	 * setting their lengths to zero.
+	 */
+	pdata = cols[VDEV_RAIDZ_P].rc_data;
+	qdata = cols[VDEV_RAIDZ_Q].rc_data;
+	xsize = cols[x].rc_size;
+	ysize = cols[y].rc_size;
+
+	cols[VDEV_RAIDZ_P].rc_data =
+		zfs_alloc_temp(cols[VDEV_RAIDZ_P].rc_size);
+	cols[VDEV_RAIDZ_Q].rc_data =
+		zfs_alloc_temp(cols[VDEV_RAIDZ_Q].rc_size);
+	cols[x].rc_size = 0;
+	cols[y].rc_size = 0;
+
+	vdev_raidz_generate_parity_pq(cols, nparity, acols);
+
+	cols[x].rc_size = xsize;
+	cols[y].rc_size = ysize;
+
+	p = pdata;
+	q = qdata;
+	pxy = cols[VDEV_RAIDZ_P].rc_data;
+	qxy = cols[VDEV_RAIDZ_Q].rc_data;
+	xd = cols[x].rc_data;
+	yd = cols[y].rc_data;
+
+	/*
+	 * We now have:
+	 *	Pxy = P + D_x + D_y
+	 *	Qxy = Q + 2^(ndevs - 1 - x) * D_x + 2^(ndevs - 1 - y) * D_y
+	 *
+	 * We can then solve for D_x:
+	 *	D_x = A * (P + Pxy) + B * (Q + Qxy)
+	 * where
+	 *	A = 2^(x - y) * (2^(x - y) + 1)^-1
+	 *	B = 2^(ndevs - 1 - x) * (2^(x - y) + 1)^-1
+	 *
+	 * With D_x in hand, we can easily solve for D_y:
+	 *	D_y = P + Pxy + D_x
+	 */
+
+	a = vdev_raidz_pow2[255 + x - y];
+	b = vdev_raidz_pow2[255 - (acols - 1 - x)];
+	tmp = 255 - vdev_raidz_log2[a ^ 1];
+
+	aexp = vdev_raidz_log2[vdev_raidz_exp2(a, tmp)];
+	bexp = vdev_raidz_log2[vdev_raidz_exp2(b, tmp)];
+
+	for (i = 0; i < xsize; i++, p++, q++, pxy++, qxy++, xd++, yd++) {
+		*xd = vdev_raidz_exp2(*p ^ *pxy, aexp) ^
+		    vdev_raidz_exp2(*q ^ *qxy, bexp);
+
+		if (i < ysize)
+			*yd = *p ^ *pxy ^ *xd;
+	}
+
+	/*
+	 * Restore the saved parity data.
+	 */
+	cols[VDEV_RAIDZ_P].rc_data = pdata;
+	cols[VDEV_RAIDZ_Q].rc_data = qdata;
+}
+
+static int
+vdev_raidz_read(vdev_t *vdev, const blkptr_t *bp, void *buf,
+    off_t offset, size_t bytes)
+{
+	size_t psize = BP_GET_PSIZE(bp);
+	vdev_t *kid;
+	int unit_shift = vdev->v_ashift;
+	int dcols = vdev->v_nchildren;
+	int nparity = vdev->v_nparity;
+	int missingdata, missingparity;
+	int parity_errors, data_errors, unexpected_errors, total_errors;
+	int parity_untried;
+	uint64_t b = offset >> unit_shift;
+	uint64_t s = psize >> unit_shift;
+	uint64_t f = b % dcols;
+	uint64_t o = (b / dcols) << unit_shift;
+	int q, r, c, c1, bc, col, acols, coff, devidx, asize, n;
+	static raidz_col_t cols[16];
+	raidz_col_t *rc, *rc1;
+
+	q = s / (dcols - nparity);
+	r = s - q * (dcols - nparity);
+	bc = (r == 0 ? 0 : r + nparity);
+
+	acols = (q == 0 ? bc : dcols);
+	asize = 0;
+	
+	for (c = 0; c < acols; c++) {
+		col = f + c;
+		coff = o;
+		if (col >= dcols) {
+			col -= dcols;
+			coff += 1ULL << unit_shift;
+		}
+		cols[c].rc_devidx = col;
+		cols[c].rc_offset = coff;
+		cols[c].rc_size = (q + (c < bc)) << unit_shift;
+		cols[c].rc_data = NULL;
+		cols[c].rc_error = 0;
+		cols[c].rc_tried = 0;
+		cols[c].rc_skipped = 0;
+		asize += cols[c].rc_size;
+	}
+
+	asize = roundup(asize, (nparity + 1) << unit_shift);
+
+	for (c = 0; c < nparity; c++) {
+		cols[c].rc_data = zfs_alloc_temp(cols[c].rc_size);
+	}
+
+	cols[c].rc_data = buf;
+
+	for (c = c + 1; c < acols; c++)
+		cols[c].rc_data = (char *)cols[c - 1].rc_data +
+		    cols[c - 1].rc_size;
+
+	/*
+	 * If all data stored spans all columns, there's a danger that
+	 * parity will always be on the same device and, since parity
+	 * isn't read during normal operation, that that device's I/O
+	 * bandwidth won't be used effectively. We therefore switch
+	 * the parity every 1MB.
+	 *
+	 * ... at least that was, ostensibly, the theory. As a
+	 * practical matter unless we juggle the parity between all
+	 * devices evenly, we won't see any benefit. Further,
+	 * occasional writes that aren't a multiple of the LCM of the
+	 * number of children and the minimum stripe width are
+	 * sufficient to avoid pessimal behavior.  Unfortunately, this
+	 * decision created an implicit on-disk format requirement
+	 * that we need to support for all eternity, but only for
+	 * single-parity RAID-Z.
+	 */
+	//ASSERT(acols >= 2);
+	//ASSERT(cols[0].rc_size == cols[1].rc_size);
+
+	if (nparity == 1 && (offset & (1ULL << 20))) {
+		devidx = cols[0].rc_devidx;
+		o = cols[0].rc_offset;
+		cols[0].rc_devidx = cols[1].rc_devidx;
+		cols[0].rc_offset = cols[1].rc_offset;
+		cols[1].rc_devidx = devidx;
+		cols[1].rc_offset = o;
+	}
+
+	/*
+	 * Iterate over the columns in reverse order so that we hit
+	 * the parity last -- any errors along the way will force us
+	 * to read the parity data.
+	 */
+	missingdata = 0;
+	missingparity = 0;
+	for (c = acols - 1; c >= 0; c--) {
+		rc = &cols[c];
+		devidx = rc->rc_devidx;
+		STAILQ_FOREACH(kid, &vdev->v_children, v_childlink)
+			if (kid->v_id == devidx)
+				break;
+		if (kid == NULL || kid->v_state != VDEV_STATE_HEALTHY) {
+			if (c >= nparity)
+				missingdata++;
+			else
+				missingparity++;
+			rc->rc_error = ENXIO;
+			rc->rc_tried = 1;	/* don't even try */
+			rc->rc_skipped = 1;
+			continue;
+		}
+#if 0
+		/*
+		 * Too hard for the bootcode
+		 */
+		if (vdev_dtl_contains(&cvd->vdev_dtl_map, bp->blk_birth, 1)) {
+			if (c >= nparity)
+				rm->rm_missingdata++;
+			else
+				rm->rm_missingparity++;
+			rc->rc_error = ESTALE;
+			rc->rc_skipped = 1;
+			continue;

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***


More information about the svn-src-all mailing list