From 6e54750c2f6fc37e072849e954eb7ca95c077956 Mon Sep 17 00:00:00 2001
From: Toomas Soome <tsoome@me.com>
Date: Fri, 15 Sep 2017 14:00:56 +0300
Subject: [PATCH] grub zfs reader should support large_dnode

---
 grub-core/fs/zfs/zfs.c   |  9 ++++----
 include/grub/zfs/dnode.h | 54 +++++++++++++++++++++++++++++++++++++++++-------
 2 files changed, 52 insertions(+), 11 deletions(-)

diff --git a/grub-core/fs/zfs/zfs.c b/grub-core/fs/zfs/zfs.c
index 6e1fff9e9..f058c19dd 100644
--- a/grub-core/fs/zfs/zfs.c
+++ b/grub-core/fs/zfs/zfs.c
@@ -285,6 +285,7 @@ static const char *spa_feature_names[] = {
   "com.delphix:embedded_data",
   "com.delphix:extensible_dataset",
   "org.open-zfs:large_blocks",
+  "org.zfsonlinux:large_dnode",
   NULL
 };
 
@@ -2936,7 +2937,7 @@ dnode_get_path (struct subvolume *subvol, const char *path_in, dnode_end_t *dn,
 	    }
 	  else if (dnode_path->dn.dn.dn_flags & DNODE_FLAG_SPILL_BLKPTR)
 	    {
-	      blkptr_t *bp = &dnode_path->dn.dn.dn_spill;
+	      blkptr_t *bp = DN_SPILL_BLKPTR(&dnode_path->dn.dn);
 	      
 	      err = zio_read (bp, dnode_path->dn.endian, &sahdrp, NULL, data);
 	      if (err)
@@ -3819,7 +3820,7 @@ grub_zfs_open (struct grub_file *file, const char *fsfilename)
 	}
       else if (data->dnode.dn.dn_flags & DNODE_FLAG_SPILL_BLKPTR)
 	{
-	  blkptr_t *bp = &data->dnode.dn.dn_spill;
+	  blkptr_t *bp = DN_SPILL_BLKPTR(&data->dnode.dn);
 
 	  err = zio_read (bp, data->dnode.endian, &sahdrp, NULL, data);
 	  if (err)
@@ -4015,7 +4016,7 @@ fill_fs_info (struct grub_dirhook_info *info,
 	}
       else if (dn.dn.dn_flags & DNODE_FLAG_SPILL_BLKPTR)
 	{
-	  blkptr_t *bp = &dn.dn.dn_spill;
+	  blkptr_t *bp = DN_SPILL_BLKPTR(&dn.dn);
 
 	  err = zio_read (bp, dn.endian, &sahdrp, NULL, data);
 	  if (err)
@@ -4068,7 +4069,7 @@ iterate_zap (const char *name, grub_uint64_t val, struct grub_zfs_dir_ctx *ctx)
 	}
       else if (dn.dn.dn_flags & DNODE_FLAG_SPILL_BLKPTR)
 	{
-	  blkptr_t *bp = &dn.dn.dn_spill;
+	  blkptr_t *bp = DN_SPILL_BLKPTR(&dn.dn);
 
 	  err = zio_read (bp, dn.endian, &sahdrp, NULL, ctx->data);
 	  if (err)
diff --git a/include/grub/zfs/dnode.h b/include/grub/zfs/dnode.h
index e4993df1e..fb2fd7329 100644
--- a/include/grub/zfs/dnode.h
+++ b/include/grub/zfs/dnode.h
@@ -35,9 +35,19 @@
 /*
  * Derived constants.
  */
-#define	DNODE_SIZE	(1 << DNODE_SHIFT)
-#define	DN_MAX_NBLKPTR	((DNODE_SIZE - DNODE_CORE_SIZE) >> SPA_BLKPTRSHIFT)
-#define	DN_MAX_BONUSLEN	(DNODE_SIZE - DNODE_CORE_SIZE - (1 << SPA_BLKPTRSHIFT))
+#define	DNODE_MIN_SIZE		(1 << DNODE_SHIFT)
+#define	DNODE_MAX_SIZE		(1 << DNODE_BLOCK_SHIFT)
+#define	DNODE_BLOCK_SIZE	(1 << DNODE_BLOCK_SHIFT)
+#define	DNODE_MIN_SLOTS		(DNODE_MIN_SIZE >> DNODE_SHIFT)
+#define	DNODE_MAX_SLOTS		(DNODE_MAX_SIZE >> DNODE_SHIFT)
+#define	DN_BONUS_SIZE(dnsize)	((dnsize) - DNODE_CORE_SIZE - \
+	(1 << SPA_BLKPTRSHIFT))
+#define	DN_SLOTS_TO_BONUSLEN(slots)	DN_BONUS_SIZE((slots) << DNODE_SHIFT)
+#define	DN_OLD_MAX_BONUSLEN	(DN_BONUS_SIZE(DNODE_MIN_SIZE))
+#define	DN_MAX_NBLKPTR		((DNODE_MIN_SIZE - DNODE_CORE_SIZE) >> \
+	SPA_BLKPTRSHIFT)
+#define	DN_MAX_OBJECT		(1ULL << DN_MAX_OBJECT_SHIFT)
+#define	DN_ZERO_BONUSLEN	(DN_BONUS_SIZE(DNODE_MAX_SIZE) + 1)
 
 #define	DNODES_PER_BLOCK_SHIFT	(DNODE_BLOCK_SHIFT - DNODE_SHIFT)
 #define	DNODES_PER_BLOCK	(1ULL << DNODES_PER_BLOCK_SHIFT)
@@ -58,7 +68,8 @@ typedef struct dnode_phys {
 	grub_uint8_t dn_flags;		/* DNODE_FLAG_* */
 	grub_uint16_t dn_datablkszsec;	/* data block size in 512b sectors */
 	grub_uint16_t dn_bonuslen;		/* length of dn_bonus */
-	grub_uint8_t dn_pad2[4];
+	grub_uint8_t dn_extra_slots;	/* # of subsequent slots consumed */
+	grub_uint8_t dn_pad2[3];
 
 	/* accounting is protected by dn_dirty_mtx */
 	grub_uint64_t dn_maxblkid;		/* largest allocated block ID */
@@ -66,9 +77,38 @@ typedef struct dnode_phys {
 
 	grub_uint64_t dn_pad3[4];
 
-	blkptr_t dn_blkptr[1];
-	grub_uint8_t dn_bonus[DN_MAX_BONUSLEN - sizeof (blkptr_t)];
-	blkptr_t dn_spill;
+	/*
+	 * The tail region is 448 bytes for a 512 byte dnode, and
+	 * correspondingly larger for larger dnode sizes. The spill
+	 * block pointer, when present, is always at the end of the tail
+	 * region. There are three ways this space may be used, using
+	 * a 512 byte dnode for this diagram:
+	 *
+	 * 0       64      128     192     256     320     384     448 (offset)
+	 * +---------------+---------------+---------------+-------+
+	 * | dn_blkptr[0]  | dn_blkptr[1]  | dn_blkptr[2]  | /     |
+	 * +---------------+---------------+---------------+-------+
+	 * | dn_blkptr[0]  | dn_bonus[0..319]                      |
+	 * +---------------+-----------------------+---------------+
+	 * | dn_blkptr[0]  | dn_bonus[0..191]      | dn_spill      |
+	 * +---------------+-----------------------+---------------+
+	*/
+	union {
+		blkptr_t dn_blkptr[1+DN_OLD_MAX_BONUSLEN/sizeof (blkptr_t)];
+		struct {
+			blkptr_t __dn_ignore1;
+			uint8_t dn_bonus[DN_OLD_MAX_BONUSLEN];
+		};
+		struct {
+		blkptr_t __dn_ignore2;
+		uint8_t __dn_ignore3[DN_OLD_MAX_BONUSLEN -
+		sizeof (blkptr_t)];
+			blkptr_t dn_spill;
+		};
+	};
 } dnode_phys_t;
 
+#define	DN_SPILL_BLKPTR(dnp)	(blkptr_t *)((char *)(dnp) + \
+	(((dnp)->dn_extra_slots + 1) << DNODE_SHIFT) - (1 << SPA_BLKPTRSHIFT))
+
 #endif	/* _SYS_DNODE_H */
-- 
2.13.5

