Re: [PATCH V10] Add support for BTRFS raid5/6 to GRUB

2018-10-31 Thread Goffredo Baroncelli
On 31/10/2018 13.06, Daniel Kiper wrote:
[...]
> 
> v11 pushed.
> 
> Goffredo, thank you for doing the work.

Great ! Many thanks for your support !!

> 
> Nick, you can go ahead and rebase yours patchset.
> 
> Daniel
> 

BR
G.Baroncelli
-- 
gpg @keyserver.linux.it: Goffredo Baroncelli 
Key fingerprint BBF5 1610 0B64 DAC6 5F7D  17B2 0EDA 9B37 8B82 E0B5

___
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel


[PATCH 1/9] btrfs: Add support for reading a filesystem with a RAID 5 or RAID 6 profile.

2018-10-22 Thread Goffredo Baroncelli
From: Goffredo Baroncelli 

Signed-off-by: Goffredo Baroncelli 
Signed-off-by: Daniel Kiper 
Reviewed-by: Daniel Kiper 
---
 grub-core/fs/btrfs.c | 73 
 1 file changed, 73 insertions(+)

diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
index be195448d..9122169aa 100644
--- a/grub-core/fs/btrfs.c
+++ b/grub-core/fs/btrfs.c
@@ -119,6 +119,8 @@ struct grub_btrfs_chunk_item
 #define GRUB_BTRFS_CHUNK_TYPE_RAID1 0x10
 #define GRUB_BTRFS_CHUNK_TYPE_DUPLICATED0x20
 #define GRUB_BTRFS_CHUNK_TYPE_RAID100x40
+#define GRUB_BTRFS_CHUNK_TYPE_RAID5 0x80
+#define GRUB_BTRFS_CHUNK_TYPE_RAID6 0x100
   grub_uint8_t dummy2[0xc];
   grub_uint16_t nstripes;
   grub_uint16_t nsubstripes;
@@ -764,6 +766,77 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, 
grub_disk_addr_t addr,
  stripe_offset = low + chunk_stripe_length
* high;
  csize = chunk_stripe_length - low;
+ break;
+   }
+ case GRUB_BTRFS_CHUNK_TYPE_RAID5:
+ case GRUB_BTRFS_CHUNK_TYPE_RAID6:
+   {
+ grub_uint64_t nparities, stripe_nr, high, low;
+
+ redundancy = 1;   /* no redundancy for now */
+
+ if (grub_le_to_cpu64 (chunk->type) & GRUB_BTRFS_CHUNK_TYPE_RAID5)
+   {
+ grub_dprintf ("btrfs", "RAID5\n");
+ nparities = 1;
+   }
+ else
+   {
+ grub_dprintf ("btrfs", "RAID6\n");
+ nparities = 2;
+   }
+
+ /*
+  * RAID 6 layout consists of several stripes spread over
+  * the disks, e.g.:
+  *
+  *   Disk_0  Disk_1  Disk_2  Disk_3
+  * A0  B0  P0  Q0
+  * Q1  A1  B1  P1
+  * P2  Q2  A2  B2
+  *
+  * Note: placement of the parities depend on row number.
+  *
+  * Pay attention that the btrfs terminology may differ from
+  * terminology used in other RAID implementations, e.g. LVM,
+  * dm or md. The main difference is that btrfs calls contiguous
+  * block of data on a given disk, e.g. A0, stripe instead of 
chunk.
+  *
+  * The variables listed below have following meaning:
+  *   - stripe_nr is the stripe number excluding the parities
+  * (A0 = 0, B0 = 1, A1 = 2, B1 = 3, etc.),
+  *   - high is the row number (0 for A0...Q0, 1 for Q1...P1, 
etc.),
+  *   - stripen is the disk number in a row (0 for A0, Q1, P2,
+  * 1 for B0, A1, Q2, etc.),
+  *   - off is the logical address to read,
+  *   - chunk_stripe_length is the size of a stripe (typically 64 
KiB),
+  *   - nstripes is the number of disks in a row,
+  *   - low is the offset of the data inside a stripe,
+  *   - stripe_offset is the data offset in an array,
+  *   - csize is the "potential" data to read; it will be reduced
+  * to size if the latter is smaller,
+  *   - nparities is the number of parities (1 for RAID 5, 2 for
+  * RAID 6); used only in RAID 5/6 code.
+  */
+ stripe_nr = grub_divmod64 (off, chunk_stripe_length, );
+
+ /*
+  * stripen is computed without the parities
+  * (0 for A0, A1, A2, 1 for B0, B1, B2, etc.).
+  */
+ high = grub_divmod64 (stripe_nr, nstripes - nparities, );
+
+ /*
+  * The stripes are spread over the disks. Every each row their
+  * positions are shifted by 1 place. So, the real disks number
+  * change. Hence, we have to take into account current row number
+  * modulo nstripes (0 for A0, 1 for A1, 2 for A2, etc.).
+  */
+ grub_divmod64 (high + stripen, nstripes, );
+
+ stripe_offset = low + chunk_stripe_length * high;
+ csize = chunk_stripe_length - low;
+
  break;
}
  default:
-- 
2.19.1


___
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel


[PATCH 7/9] btrfs: Add support for recovery for a RAID 5 btrfs profiles.

2018-10-22 Thread Goffredo Baroncelli
From: Goffredo Baroncelli 

Add support for recovery for a RAID 5 btrfs profile. In addition
it is added some code as preparatory work for RAID 6 recovery code.

Signed-off-by: Goffredo Baroncelli 
Reviewed-by: Daniel Kiper 
---
 grub-core/fs/btrfs.c | 159 +--
 1 file changed, 154 insertions(+), 5 deletions(-)

diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
index ea97f0502..965888da4 100644
--- a/grub-core/fs/btrfs.c
+++ b/grub-core/fs/btrfs.c
@@ -29,6 +29,7 @@
 #include 
 #include 
 #include 
+#include 
 
 GRUB_MOD_LICENSE ("GPLv3+");
 
@@ -665,6 +666,138 @@ btrfs_read_from_chunk (struct grub_btrfs_data *data,
 return err;
 }
 
+struct raid56_buffer {
+  void *buf;
+  int  data_is_valid;
+};
+
+static void
+rebuild_raid5 (char *dest, struct raid56_buffer *buffers,
+  grub_uint64_t nstripes, grub_uint64_t csize)
+{
+  grub_uint64_t i;
+  int first;
+
+  for(i = 0; buffers[i].data_is_valid && i < nstripes; i++);
+
+  if (i == nstripes)
+{
+  grub_dprintf ("btrfs", "called rebuild_raid5(), but all disks are OK\n");
+  return;
+}
+
+  grub_dprintf ("btrfs", "rebuilding RAID 5 stripe #%" PRIuGRUB_UINT64_T "\n", 
i);
+
+  for (i = 0, first = 1; i < nstripes; i++)
+{
+  if (!buffers[i].data_is_valid)
+   continue;
+
+  if (first) {
+   grub_memcpy(dest, buffers[i].buf, csize);
+   first = 0;
+  } else
+   grub_crypto_xor (dest, dest, buffers[i].buf, csize);
+}
+}
+
+static grub_err_t
+raid56_read_retry (struct grub_btrfs_data *data,
+  struct grub_btrfs_chunk_item *chunk,
+  grub_uint64_t stripe_offset,
+  grub_uint64_t csize, void *buf)
+{
+  struct raid56_buffer *buffers;
+  grub_uint64_t nstripes = grub_le_to_cpu16 (chunk->nstripes);
+  grub_uint64_t chunk_type = grub_le_to_cpu64 (chunk->type);
+  grub_err_t ret = GRUB_ERR_OUT_OF_MEMORY;
+  grub_uint64_t i, failed_devices;
+
+  buffers = grub_zalloc (sizeof(*buffers) * nstripes);
+  if (!buffers)
+goto cleanup;
+
+  for (i = 0; i < nstripes; i++)
+{
+  buffers[i].buf = grub_zalloc (csize);
+  if (!buffers[i].buf)
+   goto cleanup;
+}
+
+  for (failed_devices = 0, i = 0; i < nstripes; i++)
+{
+  struct grub_btrfs_chunk_stripe *stripe;
+  grub_disk_addr_t paddr;
+  grub_device_t dev;
+  grub_err_t err;
+
+  /* The struct grub_btrfs_chunk_stripe array lives behind struct
+grub_btrfs_chunk_item. */
+  stripe = (struct grub_btrfs_chunk_stripe *) (chunk + 1) + i;
+
+  paddr = grub_le_to_cpu64 (stripe->offset) + stripe_offset;
+  grub_dprintf ("btrfs", "reading paddr %" PRIxGRUB_UINT64_T
+" from stripe ID %" PRIxGRUB_UINT64_T "\n", paddr,
+stripe->device_id);
+
+  dev = find_device (data, stripe->device_id);
+  if (!dev)
+   {
+ grub_dprintf ("btrfs", "stripe %" PRIuGRUB_UINT64_T " FAILED (dev ID 
%"
+   PRIxGRUB_UINT64_T ")\n", i, stripe->device_id);
+ failed_devices++;
+ continue;
+   }
+
+  err = grub_disk_read (dev->disk, paddr >> GRUB_DISK_SECTOR_BITS,
+   paddr & (GRUB_DISK_SECTOR_SIZE - 1),
+   csize, buffers[i].buf);
+  if (err == GRUB_ERR_NONE)
+   {
+ buffers[i].data_is_valid = 1;
+ grub_dprintf ("btrfs", "stripe %" PRIuGRUB_UINT64_T " OK (dev ID %"
+   PRIxGRUB_UINT64_T ")\n", i, stripe->device_id);
+   }
+  else
+   {
+ grub_dprintf ("btrfs", "stripe %" PRIuGRUB_UINT64_T
+   " READ FAILED (dev ID %" PRIxGRUB_UINT64_T ")\n", i,
+   stripe->device_id);
+ failed_devices++;
+   }
+}
+
+  if (failed_devices > 1 && (chunk_type & GRUB_BTRFS_CHUNK_TYPE_RAID5))
+{
+  grub_dprintf ("btrfs",
+   "not enough disks for RAID 5: total %" PRIuGRUB_UINT64_T
+   ", missing %" PRIuGRUB_UINT64_T "\n",
+   nstripes, failed_devices);
+  ret = GRUB_ERR_READ_ERROR;
+  goto cleanup;
+}
+  else
+grub_dprintf ("btrfs",
+ "enough disks for RAID 5: total %"
+ PRIuGRUB_UINT64_T ", missing %" PRIuGRUB_UINT64_T "\n",
+ nstripes, failed_devices);
+
+  /* We have enough disks. So, rebuild the data. */
+  if (chunk_type & GRUB_BTRFS_CHUNK_TYPE_RAID5)
+rebuild_raid5 (buf, buffers, nstripes, csize);
+  else
+grub_dprintf ("btrfs", "called rebuild_raid6(), NOT IMPLEMENTED\n");

[PATCH 6/9] btrfs: Refactor the code that read from disk

2018-10-22 Thread Goffredo Baroncelli
From: Goffredo Baroncelli 

Move the code in charge to read the data from disk into a separate
function. This helps to separate the error handling logic (which depends on
the different raid profiles) from the read from disk logic.
Refactoring this code increases the general readability too.

This is a preparatory patch, to help the adding of the RAID 5/6 recovery
code.

Signed-off-by: Goffredo Baroncelli 
Reviewed-by: Daniel Kiper 
---
 grub-core/fs/btrfs.c | 75 ++--
 1 file changed, 44 insertions(+), 31 deletions(-)

diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
index dde0edd03..ea97f0502 100644
--- a/grub-core/fs/btrfs.c
+++ b/grub-core/fs/btrfs.c
@@ -625,6 +625,46 @@ find_device (struct grub_btrfs_data *data, grub_uint64_t 
id)
   return ctx.dev_found;
 }
 
+static grub_err_t
+btrfs_read_from_chunk (struct grub_btrfs_data *data,
+  struct grub_btrfs_chunk_item *chunk,
+  grub_uint64_t stripen, grub_uint64_t stripe_offset,
+  int redundancy, grub_uint64_t csize,
+  void *buf)
+{
+struct grub_btrfs_chunk_stripe *stripe;
+grub_disk_addr_t paddr;
+grub_device_t dev;
+grub_err_t err;
+
+stripe = (struct grub_btrfs_chunk_stripe *) (chunk + 1);
+/* Right now the redundancy handling is easy.
+   With RAID5-like it will be more difficult.  */
+stripe += stripen + redundancy;
+
+paddr = grub_le_to_cpu64 (stripe->offset) + stripe_offset;
+
+grub_dprintf ("btrfs", "stripe %" PRIxGRUB_UINT64_T
+ " maps to 0x%" PRIxGRUB_UINT64_T
+ ". Reading paddr 0x%" PRIxGRUB_UINT64_T "\n",
+ stripen, stripe->offset, paddr);
+
+dev = find_device (data, stripe->device_id);
+if (!dev)
+  {
+   grub_dprintf ("btrfs",
+ "couldn't find a necessary member device "
+ "of multi-device filesystem\n");
+   grub_errno = GRUB_ERR_NONE;
+   return GRUB_ERR_READ_ERROR;
+  }
+
+err = grub_disk_read (dev->disk, paddr >> GRUB_DISK_SECTOR_BITS,
+ paddr & (GRUB_DISK_SECTOR_SIZE - 1),
+ csize, buf);
+return err;
+}
+
 static grub_err_t
 grub_btrfs_read_logical (struct grub_btrfs_data *data, grub_disk_addr_t addr,
 void *buf, grub_size_t size, int recursion_depth)
@@ -638,7 +678,6 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, 
grub_disk_addr_t addr,
   grub_err_t err = 0;
   struct grub_btrfs_key key_out;
   int challoc = 0;
-  grub_device_t dev;
   struct grub_btrfs_key key_in;
   grub_size_t chsize;
   grub_disk_addr_t chaddr;
@@ -884,36 +923,10 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, 
grub_disk_addr_t addr,
 
for (i = 0; i < redundancy; i++)
  {
-   struct grub_btrfs_chunk_stripe *stripe;
-   grub_disk_addr_t paddr;
-
-   stripe = (struct grub_btrfs_chunk_stripe *) (chunk + 1);
-   /* Right now the redundancy handling is easy.
-  With RAID5-like it will be more difficult.  */
-   stripe += stripen + i;
-
-   paddr = grub_le_to_cpu64 (stripe->offset) + stripe_offset;
-
-   grub_dprintf ("btrfs", "stripe %" PRIxGRUB_UINT64_T
- " maps to 0x%" PRIxGRUB_UINT64_T "\n",
- stripen, stripe->offset);
-   grub_dprintf ("btrfs", "reading paddr 0x%" PRIxGRUB_UINT64_T
- "\n", paddr);
-
-   dev = find_device (data, stripe->device_id);
-   if (!dev)
- {
-   grub_dprintf ("btrfs",
- "couldn't find a necessary member device "
- "of multi-device filesystem\n");
-   err = grub_errno;
-   grub_errno = GRUB_ERR_NONE;
-   continue;
- }
-
-   err = grub_disk_read (dev->disk, paddr >> GRUB_DISK_SECTOR_BITS,
- paddr & (GRUB_DISK_SECTOR_SIZE - 1),
- csize, buf);
+   err = btrfs_read_from_chunk (data, chunk, stripen,
+stripe_offset,
+i, /* redundancy */
+csize, buf);
if (!err)
  break;
grub_errno = GRUB_ERR_NONE;
-- 
2.19.1


___
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel


[PATCH 3/9] btrfs: Move the error logging from find_device() to its caller.

2018-10-22 Thread Goffredo Baroncelli
From: Goffredo Baroncelli 

The caller knows better if this error is fatal or not, i.e. another disk is
available or not.

This is a preparatory patch.

Signed-off-by: Goffredo Baroncelli 
Reviewed-by: Daniel Kiper 
---
 grub-core/fs/btrfs.c | 10 --
 1 file changed, 4 insertions(+), 6 deletions(-)

diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
index 0cbf3551a..6b6e91cd1 100644
--- a/grub-core/fs/btrfs.c
+++ b/grub-core/fs/btrfs.c
@@ -603,12 +603,7 @@ find_device (struct grub_btrfs_data *data, grub_uint64_t 
id, int do_rescan)
   if (do_rescan)
 grub_device_iterate (find_device_iter, );
   if (!ctx.dev_found)
-{
-  grub_error (GRUB_ERR_BAD_FS,
- N_("couldn't find a necessary member device "
-"of multi-device filesystem"));
-  return NULL;
-}
+return NULL;
   data->n_devices_attached++;
   if (data->n_devices_attached > data->n_devices_allocated)
 {
@@ -905,6 +900,9 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, 
grub_disk_addr_t addr,
dev = find_device (data, stripe->device_id, j);
if (!dev)
  {
+   grub_dprintf ("btrfs",
+ "couldn't find a necessary member device "
+ "of multi-device filesystem\n");
err = grub_errno;
grub_errno = GRUB_ERR_NONE;
continue;
-- 
2.19.1


___
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel


[PATCH 4/9] btrfs: Avoid a rescan for a device which was already not found.

2018-10-22 Thread Goffredo Baroncelli
From: Goffredo Baroncelli 

Currently read from missing device triggers rescan. However, it is never
recorded that the device is missing. So, each read of a missing device
triggers rescan again and again. This behavior causes a lot of unneeded
rescans leading to huge slowdowns.

This patch fixes above mentioned issue. Information about missing devices
is stored in the data->devices_attached[] array as NULL value in dev
member. Rescan is triggered only if no information is found for a given
device. This means that only first time read triggers rescan.

The patch drops premature return. This way data->devices_attached[] is
filled even when a given device is missing.

Signed-off-by: Goffredo Baroncelli 
Signed-off-by: Daniel Kiper 
Reviewed-by: Daniel Kiper 
---
 grub-core/fs/btrfs.c | 17 +
 1 file changed, 9 insertions(+), 8 deletions(-)

diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
index 6b6e91cd1..81f3bc120 100644
--- a/grub-core/fs/btrfs.c
+++ b/grub-core/fs/btrfs.c
@@ -588,7 +588,7 @@ find_device_iter (const char *name, void *data)
 }
 
 static grub_device_t
-find_device (struct grub_btrfs_data *data, grub_uint64_t id, int do_rescan)
+find_device (struct grub_btrfs_data *data, grub_uint64_t id)
 {
   struct find_device_ctx ctx = {
 .data = data,
@@ -600,10 +600,9 @@ find_device (struct grub_btrfs_data *data, grub_uint64_t 
id, int do_rescan)
   for (i = 0; i < data->n_devices_attached; i++)
 if (id == data->devices_attached[i].id)
   return data->devices_attached[i].dev;
-  if (do_rescan)
-grub_device_iterate (find_device_iter, );
-  if (!ctx.dev_found)
-return NULL;
+
+  grub_device_iterate (find_device_iter, );
+
   data->n_devices_attached++;
   if (data->n_devices_attached > data->n_devices_allocated)
 {
@@ -615,7 +614,8 @@ find_device (struct grub_btrfs_data *data, grub_uint64_t 
id, int do_rescan)
* sizeof (data->devices_attached[0]));
   if (!data->devices_attached)
{
- grub_device_close (ctx.dev_found);
+ if (ctx.dev_found)
+   grub_device_close (ctx.dev_found);
  data->devices_attached = tmp;
  return NULL;
}
@@ -897,7 +897,7 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, 
grub_disk_addr_t addr,
  " for laddr 0x%" PRIxGRUB_UINT64_T "\n", paddr,
  addr);
 
-   dev = find_device (data, stripe->device_id, j);
+   dev = find_device (data, stripe->device_id);
if (!dev)
  {
grub_dprintf ("btrfs",
@@ -974,7 +974,8 @@ grub_btrfs_unmount (struct grub_btrfs_data *data)
   unsigned i;
   /* The device 0 is closed one layer upper.  */
   for (i = 1; i < data->n_devices_attached; i++)
-grub_device_close (data->devices_attached[i].dev);
+if (data->devices_attached[i].dev)
+grub_device_close (data->devices_attached[i].dev);
   grub_free (data->devices_attached);
   grub_free (data->extent);
   grub_free (data);
-- 
2.19.1


___
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel


Re: [PATCH V10] Add support for BTRFS raid5/6 to GRUB

2018-10-22 Thread Goffredo Baroncelli
On 22/10/2018 13.02, Daniel Kiper wrote:
> On Thu, Oct 18, 2018 at 07:55:32PM +0200, Goffredo Baroncelli wrote:
>>
>> Hi All,
>>
>> the aim of this patches set is to provide support for a BTRFS raid5/6
>> filesystem in GRUB.
[...]
> 
> In general whole patch series LGTM. +/- some nit picks including changes
> for patch #7. If you are OK with them and there are no objections then
> I will apply the patches in a week or so.

Good news; I will update the patch 7 and I will send patches set v11


BR
G.Baroncelli

> 
> Thank you for doing the work.
> 
> Daniel
> 
> ___
> Grub-devel mailing list
> Grub-devel@gnu.org
> https://lists.gnu.org/mailman/listinfo/grub-devel
> 


-- 
gpg @keyserver.linux.it: Goffredo Baroncelli 
Key fingerprint BBF5 1610 0B64 DAC6 5F7D  17B2 0EDA 9B37 8B82 E0B5

___
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel


[PATCH 7/9] btrfs: Add support for recovery for a RAID 5 btrfs profiles.

2018-10-18 Thread Goffredo Baroncelli
From: Goffredo Baroncelli 

Add support for recovery for a RAID 5 btrfs profile. In addition
it is added some code as preparatory work for RAID 6 recovery code.

Signed-off-by: Goffredo Baroncelli 
---
 grub-core/fs/btrfs.c | 161 +--
 1 file changed, 156 insertions(+), 5 deletions(-)

diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
index ea97f0502..b277f2904 100644
--- a/grub-core/fs/btrfs.c
+++ b/grub-core/fs/btrfs.c
@@ -29,6 +29,7 @@
 #include 
 #include 
 #include 
+#include 
 
 GRUB_MOD_LICENSE ("GPLv3+");
 
@@ -665,6 +666,140 @@ btrfs_read_from_chunk (struct grub_btrfs_data *data,
 return err;
 }
 
+struct raid56_buffer {
+  void *buf;
+  int  data_is_valid;
+};
+
+static void
+rebuild_raid5 (char *dest, struct raid56_buffer *buffers,
+  grub_uint64_t nstripes, grub_uint64_t csize)
+{
+  grub_uint64_t i;
+  int first;
+
+  for(i = 0; buffers[i].data_is_valid && i < nstripes; i++);
+
+  if (i == nstripes)
+{
+  grub_dprintf ("btrfs", "called rebuild_raid5(), but all disks are OK\n");
+  return;
+}
+
+  grub_dprintf ("btrfs", "rebuilding RAID 5 stripe #%" PRIuGRUB_UINT64_T "\n", 
i);
+
+  for (i = 0, first = 1; i < nstripes; i++)
+{
+  if (!buffers[i].data_is_valid)
+   continue;
+
+  if (first) {
+   grub_memcpy(dest, buffers[i].buf, csize);
+   first = 0;
+  } else
+   grub_crypto_xor (dest, dest, buffers[i].buf, csize);
+}
+}
+
+static grub_err_t
+raid56_read_retry (struct grub_btrfs_data *data,
+  struct grub_btrfs_chunk_item *chunk,
+  grub_uint64_t stripe_offset,
+  grub_uint64_t csize, void *buf)
+{
+  struct raid56_buffer *buffers;
+  grub_uint64_t nstripes = grub_le_to_cpu16 (chunk->nstripes);
+  grub_uint64_t chunk_type = grub_le_to_cpu64 (chunk->type);
+  grub_err_t ret = GRUB_ERR_OUT_OF_MEMORY;
+  grub_uint64_t i, failed_devices;
+
+  buffers = grub_zalloc (sizeof(*buffers) * nstripes);
+  if (!buffers)
+goto cleanup;
+
+  for (i = 0; i < nstripes; i++)
+{
+  buffers[i].buf = grub_zalloc (csize);
+  if (!buffers[i].buf)
+   goto cleanup;
+}
+
+  for (failed_devices = 0, i = 0; i < nstripes; i++)
+{
+  struct grub_btrfs_chunk_stripe *stripe;
+  grub_disk_addr_t paddr;
+  grub_device_t dev;
+  grub_err_t err;
+
+  /* The struct grub_btrfs_chunk_stripe array lives behind struct
+grub_btrfs_chunk_item. */
+  stripe = (struct grub_btrfs_chunk_stripe *) (chunk + 1) + i;
+
+  paddr = grub_le_to_cpu64 (stripe->offset) + stripe_offset;
+  grub_dprintf ("btrfs", "reading paddr %" PRIxGRUB_UINT64_T
+" from stripe ID %" PRIxGRUB_UINT64_T "\n", paddr,
+stripe->device_id);
+
+  dev = find_device (data, stripe->device_id);
+  if (!dev)
+   {
+ buffers[i].data_is_valid = 0;
+ grub_dprintf ("btrfs", "stripe %" PRIuGRUB_UINT64_T " FAILED (dev ID 
%"
+   PRIxGRUB_UINT64_T ")\n", i, stripe->device_id);
+ failed_devices++;
+ continue;
+   }
+
+  err = grub_disk_read (dev->disk, paddr >> GRUB_DISK_SECTOR_BITS,
+   paddr & (GRUB_DISK_SECTOR_SIZE - 1),
+   csize, buffers[i].buf);
+  if (err == GRUB_ERR_NONE)
+   {
+ buffers[i].data_is_valid = 1;
+ grub_dprintf ("btrfs", "stripe %" PRIuGRUB_UINT64_T " OK (dev ID %"
+   PRIxGRUB_UINT64_T ")\n", i, stripe->device_id);
+   }
+  else
+   {
+ buffers[i].data_is_valid = 0;
+ grub_dprintf ("btrfs", "stripe %" PRIuGRUB_UINT64_T
+   " FAILED (dev ID %" PRIxGRUB_UINT64_T ")\n", i,
+   stripe->device_id);
+ failed_devices++;
+   }
+}
+
+  if (failed_devices > 1 && (chunk_type & GRUB_BTRFS_CHUNK_TYPE_RAID5))
+{
+  grub_dprintf ("btrfs",
+   "not enough disks for RAID 5: total %" PRIuGRUB_UINT64_T
+   ", missing %" PRIuGRUB_UINT64_T "\n",
+   nstripes, failed_devices);
+  ret = GRUB_ERR_READ_ERROR;
+  goto cleanup;
+}
+  else
+grub_dprintf ("btrfs",
+ "enough disks for RAID 5: total %"
+ PRIuGRUB_UINT64_T ", missing %" PRIuGRUB_UINT64_T "\n",
+ nstripes, failed_devices);
+
+  /* We have enough disks. So, rebuild the data. */
+  if (chunk_type & GRUB_BTRFS_CHUNK_TYPE_RAID5)
+rebuild_raid5 (buf, buffers, nstripes, csize);
+  else
+grub_dprintf ("btrfs", &q

[PATCH 8/9] btrfs: Make more generic the code for RAID 6 rebuilding

2018-10-18 Thread Goffredo Baroncelli
From: Goffredo Baroncelli 

The original code which handles the recovery of a RAID 6 disks array
assumes that all reads are multiple of 1 << GRUB_DISK_SECTOR_BITS and it
assumes that all the I/O is done via the struct grub_diskfilter_segment.
This is not true for the btrfs code. In order to reuse the native
grub_raid6_recover() code, it is modified to not call
grub_diskfilter_read_node() directly, but to call an handler passed
as an argument.

Signed-off-by: Goffredo Baroncelli 
Reviewed-by: Daniel Kiper 
---
 grub-core/disk/raid6_recover.c | 52 ++
 include/grub/diskfilter.h  |  9 ++
 2 files changed, 43 insertions(+), 18 deletions(-)

diff --git a/grub-core/disk/raid6_recover.c b/grub-core/disk/raid6_recover.c
index aa674f6ca..0cf691ddf 100644
--- a/grub-core/disk/raid6_recover.c
+++ b/grub-core/disk/raid6_recover.c
@@ -74,14 +74,26 @@ mod_255 (unsigned x)
 }
 
 static grub_err_t
-grub_raid6_recover (struct grub_diskfilter_segment *array, int disknr, int p,
-char *buf, grub_disk_addr_t sector, grub_size_t size)
+raid6_recover_read_node (void *data, int disknr,
+   grub_uint64_t sector,
+   void *buf, grub_size_t size)
+{
+struct grub_diskfilter_segment *array = data;
+
+return grub_diskfilter_read_node (>nodes[disknr],
+ (grub_disk_addr_t)sector,
+ size >> GRUB_DISK_SECTOR_BITS, buf);
+}
+
+grub_err_t
+grub_raid6_recover_gen (void *data, grub_uint64_t nstripes, int disknr, int p,
+   char *buf, grub_uint64_t sector, grub_size_t size,
+   int layout, raid_recover_read_t read_func)
 {
   int i, q, pos;
   int bad1 = -1, bad2 = -1;
   char *pbuf = 0, *qbuf = 0;
 
-  size <<= GRUB_DISK_SECTOR_BITS;
   pbuf = grub_zalloc (size);
   if (!pbuf)
 goto quit;
@@ -91,17 +103,17 @@ grub_raid6_recover (struct grub_diskfilter_segment *array, 
int disknr, int p,
 goto quit;
 
   q = p + 1;
-  if (q == (int) array->node_count)
+  if (q == (int) nstripes)
 q = 0;
 
   pos = q + 1;
-  if (pos == (int) array->node_count)
+  if (pos == (int) nstripes)
 pos = 0;
 
-  for (i = 0; i < (int) array->node_count - 2; i++)
+  for (i = 0; i < (int) nstripes - 2; i++)
 {
   int c;
-  if (array->layout & GRUB_RAID_LAYOUT_MUL_FROM_POS)
+  if (layout & GRUB_RAID_LAYOUT_MUL_FROM_POS)
c = pos;
   else
c = i;
@@ -109,8 +121,7 @@ grub_raid6_recover (struct grub_diskfilter_segment *array, 
int disknr, int p,
 bad1 = c;
   else
 {
-  if (! grub_diskfilter_read_node (>nodes[pos], sector,
-  size >> GRUB_DISK_SECTOR_BITS, buf))
+ if (!read_func(data, pos, sector, buf, size))
 {
   grub_crypto_xor (pbuf, pbuf, buf, size);
   grub_raid_block_mulx (c, buf, size);
@@ -128,7 +139,7 @@ grub_raid6_recover (struct grub_diskfilter_segment *array, 
int disknr, int p,
 }
 
   pos++;
-  if (pos == (int) array->node_count)
+  if (pos == (int) nstripes)
 pos = 0;
 }
 
@@ -139,16 +150,14 @@ grub_raid6_recover (struct grub_diskfilter_segment 
*array, int disknr, int p,
   if (bad2 < 0)
 {
   /* One bad device */
-  if ((! grub_diskfilter_read_node (>nodes[p], sector,
-   size >> GRUB_DISK_SECTOR_BITS, buf)))
+  if (!read_func(data, p, sector, buf, size))
 {
   grub_crypto_xor (buf, buf, pbuf, size);
   goto quit;
 }
 
   grub_errno = GRUB_ERR_NONE;
-  if (grub_diskfilter_read_node (>nodes[q], sector,
-size >> GRUB_DISK_SECTOR_BITS, buf))
+  if (read_func(data, q, sector, buf, size))
 goto quit;
 
   grub_crypto_xor (buf, buf, qbuf, size);
@@ -160,14 +169,12 @@ grub_raid6_recover (struct grub_diskfilter_segment 
*array, int disknr, int p,
   /* Two bad devices */
   unsigned c;
 
-  if (grub_diskfilter_read_node (>nodes[p], sector,
-size >> GRUB_DISK_SECTOR_BITS, buf))
+  if (read_func(data, p, sector, buf, size))
 goto quit;
 
   grub_crypto_xor (pbuf, pbuf, buf, size);
 
-  if (grub_diskfilter_read_node (>nodes[q], sector,
-size >> GRUB_DISK_SECTOR_BITS, buf))
+  if (read_func(data, q, sector, buf, size))
 goto quit;
 
   grub_crypto_xor (qbuf, qbuf, buf, size);
@@ -190,6 +197,15 @@ quit:
   return grub_errno;
 }
 
+static grub_err_t
+grub_raid6_recover (struct grub_diskfilter_segment *array, int disknr, int p,
+char *buf, grub_disk_addr_t sector, grub_size_t size)
+{
+  return grub_raid6_recover_gen (array, array->n

[PATCH 4/9] btrfs: Avoid a rescan for a device which was already not found.

2018-10-18 Thread Goffredo Baroncelli
From: Goffredo Baroncelli 

Currently read from missing device triggers rescan. However, it is never
recorded that the device is missing. So, each read of a missing device
triggers rescan again and again. This behavior causes a lot of unneeded
rescans leading to huge slowdowns.

This patch fixes above mentioned issue. Information about missing devices
is stored in the data->devices_attached[] array as NULL value in dev
member. Rescan is triggered only if no information is found for a given
device. This means that only first time read triggers rescan.

The patch drops premature return. This way data->devices_attached[] is
filled even when a given device is missing.

Signed-off-by: Goffredo Baroncelli 
Signed-off-by: Daniel Kiper 
Reviewed-by: Daniel Kiper 
---
 grub-core/fs/btrfs.c | 17 +
 1 file changed, 9 insertions(+), 8 deletions(-)

diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
index 6b6e91cd1..81f3bc120 100644
--- a/grub-core/fs/btrfs.c
+++ b/grub-core/fs/btrfs.c
@@ -588,7 +588,7 @@ find_device_iter (const char *name, void *data)
 }
 
 static grub_device_t
-find_device (struct grub_btrfs_data *data, grub_uint64_t id, int do_rescan)
+find_device (struct grub_btrfs_data *data, grub_uint64_t id)
 {
   struct find_device_ctx ctx = {
 .data = data,
@@ -600,10 +600,9 @@ find_device (struct grub_btrfs_data *data, grub_uint64_t 
id, int do_rescan)
   for (i = 0; i < data->n_devices_attached; i++)
 if (id == data->devices_attached[i].id)
   return data->devices_attached[i].dev;
-  if (do_rescan)
-grub_device_iterate (find_device_iter, );
-  if (!ctx.dev_found)
-return NULL;
+
+  grub_device_iterate (find_device_iter, );
+
   data->n_devices_attached++;
   if (data->n_devices_attached > data->n_devices_allocated)
 {
@@ -615,7 +614,8 @@ find_device (struct grub_btrfs_data *data, grub_uint64_t 
id, int do_rescan)
* sizeof (data->devices_attached[0]));
   if (!data->devices_attached)
{
- grub_device_close (ctx.dev_found);
+ if (ctx.dev_found)
+   grub_device_close (ctx.dev_found);
  data->devices_attached = tmp;
  return NULL;
}
@@ -897,7 +897,7 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, 
grub_disk_addr_t addr,
  " for laddr 0x%" PRIxGRUB_UINT64_T "\n", paddr,
  addr);
 
-   dev = find_device (data, stripe->device_id, j);
+   dev = find_device (data, stripe->device_id);
if (!dev)
  {
grub_dprintf ("btrfs",
@@ -974,7 +974,8 @@ grub_btrfs_unmount (struct grub_btrfs_data *data)
   unsigned i;
   /* The device 0 is closed one layer upper.  */
   for (i = 1; i < data->n_devices_attached; i++)
-grub_device_close (data->devices_attached[i].dev);
+if (data->devices_attached[i].dev)
+grub_device_close (data->devices_attached[i].dev);
   grub_free (data->devices_attached);
   grub_free (data->extent);
   grub_free (data);
-- 
2.19.1


___
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel


[PATCH V10] Add support for BTRFS raid5/6 to GRUB

2018-10-18 Thread Goffredo Baroncelli


Hi All,

the aim of this patches set is to provide support for a BTRFS raid5/6
filesystem in GRUB.

The first patch, implements the basic support for raid5/6. I.e this works when
all the disks are present.

The next 5 patches, are preparatory ones.

The 7th patch implements the raid5 recovery for btrfs (i.e. handling the
disappearing of 1 disk).
The 8th patch makes the code for handling the raid6 recovery more generic.
The last one implements the raid6 recovery for btrfs (i.e. handling the
disappearing up to two disks).

I tested the code in grub-emu, and it works both with all the disks,
and with some disks missing. I checked the crc32 calculated from grub and
from linux and these matched. Finally I checked if the support for md raid6
still works properly, and it does (with all drives and with up to 2 drives
missing)

Comments are welcome.

Changelog
v1: initial support for btrfs raid5/6. No recovery allowed
v2: full support for btrfs raid5/6. Recovery allowed
v3: some minor cleanup suggested by Daniel Kiper; reusing the
original raid6 recovery code of grub
v4: Several spell fix; better description of the RAID layout
in btrfs, and the variables which describes the stripe
positioning; split the patch #5 in two (#5 and #6)
v5: Several spell fix; improved code comment in patch #1, small
clean up in the code
v6: Small cleanup; improved the wording in the RAID6 layout
description; in the function raid6_recover_read_buffer() avoid
a unnecessary memcpy in case of invalid data;
v7: - patch 2,3,5,6,8 received an Review-by Daniel, and were unchanged from
the last time (only minor cleanup in the commit description requested by
Daniel)
- patch 7 received some small update rearranging a for(), and some
bracket around if()
- patch 4, received an update message which explains better why NULL
is stored in data->devices_attached[]
- patch 9, received a blank line to separate better a code line from
a previous comment. A description of 'parities_pos' was added
- patch 1, received a major update about the variable meaning description
in the comment. However I suspect that we need some further review to reach
a fully agreement about this text. NB: the update are relate only to
comments
v8: - patch 2,5,6,8 received an Review-by Daniel, and were unchanged from
the last time (only minor cleanup in the commit description requested by
Daniel)
- patch 1 received some adjustement to the variables description due to
  the different terminology between BTRFS and other RAID implementatio.
  Added a description for the "nparities" variable.
- patch 3 removed some unnecessary curly brackets (change request by Daniel)
- patch 4 received an improved commit description about why and how
  the function find_device() is changed
- patch 7 received an update which transforms a i = 0; while(i..) i++; in
  for( i = 0. ; i++);
- patch 9 received an update to the comment
v9: - patch 1: update comments
- patch 4: update commit messages
- patch 7: added a comment about accessing an array of structs
  after another structs; changed if(err == GRUB_ERR_NONE) in if(!err)
  changed if(err != GRUB_ERR_NONE) in if(err)

v10:- patch 1: update comments (replace might with can)
- patch 4: add a Signed off by Daniel
- patch 7: drop an empty line; changed some text in grub_dprintf;
  reversed the logic of an if (if(!is_raid56) A else B -> if(is_raid5) B
  else A); add a space between the function name and the '('
- patch 9: update the wording in the comment; s/raid6/RAID 6/ in 
  grub_dprintf()

BR
G.Baroncelli

--
gpg @keyserver.linux.it: Goffredo Baroncelli 
Key fingerprint BBF5 1610 0B64 DAC6 5F7D  17B2 0EDA 9B37 8B82 E0B5






___
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel


[PATCH 6/9] btrfs: Refactor the code that read from disk

2018-10-18 Thread Goffredo Baroncelli
From: Goffredo Baroncelli 

Move the code in charge to read the data from disk into a separate
function. This helps to separate the error handling logic (which depends on
the different raid profiles) from the read from disk logic.
Refactoring this code increases the general readability too.

This is a preparatory patch, to help the adding of the RAID 5/6 recovery
code.

Signed-off-by: Goffredo Baroncelli 
Reviewed-by: Daniel Kiper 
---
 grub-core/fs/btrfs.c | 75 ++--
 1 file changed, 44 insertions(+), 31 deletions(-)

diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
index dde0edd03..ea97f0502 100644
--- a/grub-core/fs/btrfs.c
+++ b/grub-core/fs/btrfs.c
@@ -625,6 +625,46 @@ find_device (struct grub_btrfs_data *data, grub_uint64_t 
id)
   return ctx.dev_found;
 }
 
+static grub_err_t
+btrfs_read_from_chunk (struct grub_btrfs_data *data,
+  struct grub_btrfs_chunk_item *chunk,
+  grub_uint64_t stripen, grub_uint64_t stripe_offset,
+  int redundancy, grub_uint64_t csize,
+  void *buf)
+{
+struct grub_btrfs_chunk_stripe *stripe;
+grub_disk_addr_t paddr;
+grub_device_t dev;
+grub_err_t err;
+
+stripe = (struct grub_btrfs_chunk_stripe *) (chunk + 1);
+/* Right now the redundancy handling is easy.
+   With RAID5-like it will be more difficult.  */
+stripe += stripen + redundancy;
+
+paddr = grub_le_to_cpu64 (stripe->offset) + stripe_offset;
+
+grub_dprintf ("btrfs", "stripe %" PRIxGRUB_UINT64_T
+ " maps to 0x%" PRIxGRUB_UINT64_T
+ ". Reading paddr 0x%" PRIxGRUB_UINT64_T "\n",
+ stripen, stripe->offset, paddr);
+
+dev = find_device (data, stripe->device_id);
+if (!dev)
+  {
+   grub_dprintf ("btrfs",
+ "couldn't find a necessary member device "
+ "of multi-device filesystem\n");
+   grub_errno = GRUB_ERR_NONE;
+   return GRUB_ERR_READ_ERROR;
+  }
+
+err = grub_disk_read (dev->disk, paddr >> GRUB_DISK_SECTOR_BITS,
+ paddr & (GRUB_DISK_SECTOR_SIZE - 1),
+ csize, buf);
+return err;
+}
+
 static grub_err_t
 grub_btrfs_read_logical (struct grub_btrfs_data *data, grub_disk_addr_t addr,
 void *buf, grub_size_t size, int recursion_depth)
@@ -638,7 +678,6 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, 
grub_disk_addr_t addr,
   grub_err_t err = 0;
   struct grub_btrfs_key key_out;
   int challoc = 0;
-  grub_device_t dev;
   struct grub_btrfs_key key_in;
   grub_size_t chsize;
   grub_disk_addr_t chaddr;
@@ -884,36 +923,10 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, 
grub_disk_addr_t addr,
 
for (i = 0; i < redundancy; i++)
  {
-   struct grub_btrfs_chunk_stripe *stripe;
-   grub_disk_addr_t paddr;
-
-   stripe = (struct grub_btrfs_chunk_stripe *) (chunk + 1);
-   /* Right now the redundancy handling is easy.
-  With RAID5-like it will be more difficult.  */
-   stripe += stripen + i;
-
-   paddr = grub_le_to_cpu64 (stripe->offset) + stripe_offset;
-
-   grub_dprintf ("btrfs", "stripe %" PRIxGRUB_UINT64_T
- " maps to 0x%" PRIxGRUB_UINT64_T "\n",
- stripen, stripe->offset);
-   grub_dprintf ("btrfs", "reading paddr 0x%" PRIxGRUB_UINT64_T
- "\n", paddr);
-
-   dev = find_device (data, stripe->device_id);
-   if (!dev)
- {
-   grub_dprintf ("btrfs",
- "couldn't find a necessary member device "
- "of multi-device filesystem\n");
-   err = grub_errno;
-   grub_errno = GRUB_ERR_NONE;
-   continue;
- }
-
-   err = grub_disk_read (dev->disk, paddr >> GRUB_DISK_SECTOR_BITS,
- paddr & (GRUB_DISK_SECTOR_SIZE - 1),
- csize, buf);
+   err = btrfs_read_from_chunk (data, chunk, stripen,
+stripe_offset,
+i, /* redundancy */
+csize, buf);
if (!err)
  break;
grub_errno = GRUB_ERR_NONE;
-- 
2.19.1


___
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel


[PATCH 2/9] btrfs: Add helper to check the btrfs header.

2018-10-18 Thread Goffredo Baroncelli
From: Goffredo Baroncelli 

This helper is used in a few places to help the debugging. As
conservative approach the error is only logged.
This does not impact the error handling.

Signed-off-by: Goffredo Baroncelli 
Reviewed-by: Daniel Kiper 
---
 grub-core/fs/btrfs.c | 24 +++-
 1 file changed, 23 insertions(+), 1 deletion(-)

diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
index 9122169aa..0cbf3551a 100644
--- a/grub-core/fs/btrfs.c
+++ b/grub-core/fs/btrfs.c
@@ -77,7 +77,8 @@ struct btrfs_header
 {
   grub_btrfs_checksum_t checksum;
   grub_btrfs_uuid_t uuid;
-  grub_uint8_t dummy[0x30];
+  grub_uint64_t bytenr;
+  grub_uint8_t dummy[0x28];
   grub_uint32_t nitems;
   grub_uint8_t level;
 } GRUB_PACKED;
@@ -286,6 +287,25 @@ free_iterator (struct grub_btrfs_leaf_descriptor *desc)
   grub_free (desc->data);
 }
 
+static grub_err_t
+check_btrfs_header (struct grub_btrfs_data *data, struct btrfs_header *header,
+grub_disk_addr_t addr)
+{
+  if (grub_le_to_cpu64 (header->bytenr) != addr)
+{
+  grub_dprintf ("btrfs", "btrfs_header.bytenr is not equal node addr\n");
+  return grub_error (GRUB_ERR_BAD_FS,
+"header bytenr is not equal node addr");
+}
+  if (grub_memcmp (data->sblock.uuid, header->uuid, sizeof(grub_btrfs_uuid_t)))
+{
+  grub_dprintf ("btrfs", "btrfs_header.uuid doesn't match sblock uuid\n");
+  return grub_error (GRUB_ERR_BAD_FS,
+"header uuid doesn't match sblock uuid");
+}
+  return GRUB_ERR_NONE;
+}
+
 static grub_err_t
 save_ref (struct grub_btrfs_leaf_descriptor *desc,
  grub_disk_addr_t addr, unsigned i, unsigned m, int l)
@@ -341,6 +361,7 @@ next (struct grub_btrfs_data *data,
 
   err = grub_btrfs_read_logical (data, grub_le_to_cpu64 (node.addr),
 , sizeof (head), 0);
+  check_btrfs_header (data, , grub_le_to_cpu64 (node.addr));
   if (err)
return -err;
 
@@ -402,6 +423,7 @@ lower_bound (struct grub_btrfs_data *data,
   /* FIXME: preread few nodes into buffer. */
   err = grub_btrfs_read_logical (data, addr, , sizeof (head),
 recursion_depth + 1);
+  check_btrfs_header (data, , addr);
   if (err)
return err;
   addr += sizeof (head);
-- 
2.19.1


___
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel


[PATCH 5/9] btrfs: Move logging code in grub_btrfs_read_logical()

2018-10-18 Thread Goffredo Baroncelli
From: Goffredo Baroncelli 

A portion of the logging code is moved outside of internal for(;;). The part
that is left inside is the one which depends on the internal for(;;) index.

This is a preparatory patch. The next one will refactor the code inside
the for(;;) into an another function.

Signed-off-by: Goffredo Baroncelli 
Reviewed-by: Daniel Kiper 
---
 grub-core/fs/btrfs.c | 25 ++---
 1 file changed, 14 insertions(+), 11 deletions(-)

diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
index 81f3bc120..dde0edd03 100644
--- a/grub-core/fs/btrfs.c
+++ b/grub-core/fs/btrfs.c
@@ -870,6 +870,18 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, 
grub_disk_addr_t addr,
 
for (j = 0; j < 2; j++)
  {
+   grub_dprintf ("btrfs", "chunk 0x%" PRIxGRUB_UINT64_T
+ "+0x%" PRIxGRUB_UINT64_T
+ " (%d stripes (%d substripes) of %"
+ PRIxGRUB_UINT64_T ")\n",
+ grub_le_to_cpu64 (key->offset),
+ grub_le_to_cpu64 (chunk->size),
+ grub_le_to_cpu16 (chunk->nstripes),
+ grub_le_to_cpu16 (chunk->nsubstripes),
+ grub_le_to_cpu64 (chunk->stripe_length));
+   grub_dprintf ("btrfs", "reading laddr 0x%" PRIxGRUB_UINT64_T "\n",
+ addr);
+
for (i = 0; i < redundancy; i++)
  {
struct grub_btrfs_chunk_stripe *stripe;
@@ -882,20 +894,11 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, 
grub_disk_addr_t addr,
 
paddr = grub_le_to_cpu64 (stripe->offset) + stripe_offset;
 
-   grub_dprintf ("btrfs", "chunk 0x%" PRIxGRUB_UINT64_T
- "+0x%" PRIxGRUB_UINT64_T
- " (%d stripes (%d substripes) of %"
- PRIxGRUB_UINT64_T ") stripe %" PRIxGRUB_UINT64_T
+   grub_dprintf ("btrfs", "stripe %" PRIxGRUB_UINT64_T
  " maps to 0x%" PRIxGRUB_UINT64_T "\n",
- grub_le_to_cpu64 (key->offset),
- grub_le_to_cpu64 (chunk->size),
- grub_le_to_cpu16 (chunk->nstripes),
- grub_le_to_cpu16 (chunk->nsubstripes),
- grub_le_to_cpu64 (chunk->stripe_length),
  stripen, stripe->offset);
grub_dprintf ("btrfs", "reading paddr 0x%" PRIxGRUB_UINT64_T
- " for laddr 0x%" PRIxGRUB_UINT64_T "\n", paddr,
- addr);
+ "\n", paddr);
 
dev = find_device (data, stripe->device_id);
if (!dev)
-- 
2.19.1


___
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel


[PATCH 9/9] btrfs: Add RAID 6 recovery for a btrfs filesystem.

2018-10-18 Thread Goffredo Baroncelli
From: Goffredo Baroncelli 

Add the RAID 6 recovery, in order to use a RAID 6 filesystem even if some
disks (up to two) are missing. This code use the md RAID 6 code already
present in grub.

Signed-off-by: Goffredo Baroncelli 
Reviewed-by: Daniel Kiper 
---
 grub-core/fs/btrfs.c | 60 +++-
 1 file changed, 54 insertions(+), 6 deletions(-)

diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
index b277f2904..9419d313d 100644
--- a/grub-core/fs/btrfs.c
+++ b/grub-core/fs/btrfs.c
@@ -30,6 +30,7 @@
 #include 
 #include 
 #include 
+#include 
 
 GRUB_MOD_LICENSE ("GPLv3+");
 
@@ -701,11 +702,36 @@ rebuild_raid5 (char *dest, struct raid56_buffer *buffers,
 }
 }
 
+static grub_err_t
+raid6_recover_read_buffer (void *data, int disk_nr,
+  grub_uint64_t addr __attribute__ ((unused)),
+  void *dest, grub_size_t size)
+{
+struct raid56_buffer *buffers = data;
+
+if (!buffers[disk_nr].data_is_valid)
+   return grub_errno = GRUB_ERR_READ_ERROR;
+
+grub_memcpy(dest, buffers[disk_nr].buf, size);
+
+return grub_errno = GRUB_ERR_NONE;
+}
+
+static void
+rebuild_raid6 (struct raid56_buffer *buffers, grub_uint64_t nstripes,
+   grub_uint64_t csize, grub_uint64_t parities_pos, void *dest,
+   grub_uint64_t stripen)
+
+{
+  grub_raid6_recover_gen (buffers, nstripes, stripen, parities_pos,
+  dest, 0, csize, 0, raid6_recover_read_buffer);
+}
+
 static grub_err_t
 raid56_read_retry (struct grub_btrfs_data *data,
   struct grub_btrfs_chunk_item *chunk,
-  grub_uint64_t stripe_offset,
-  grub_uint64_t csize, void *buf)
+  grub_uint64_t stripe_offset, grub_uint64_t stripen,
+  grub_uint64_t csize, void *buf, grub_uint64_t parities_pos)
 {
   struct raid56_buffer *buffers;
   grub_uint64_t nstripes = grub_le_to_cpu16 (chunk->nstripes);
@@ -778,6 +804,15 @@ raid56_read_retry (struct grub_btrfs_data *data,
   ret = GRUB_ERR_READ_ERROR;
   goto cleanup;
 }
+  else if (failed_devices > 2 && (chunk_type & GRUB_BTRFS_CHUNK_TYPE_RAID6))
+{
+  grub_dprintf ("btrfs",
+   "not enough disks for RAID 6: total %" PRIuGRUB_UINT64_T
+   ", missing %" PRIuGRUB_UINT64_T "\n",
+   nstripes, failed_devices);
+  ret = GRUB_ERR_READ_ERROR;
+  goto cleanup;
+}
   else
 grub_dprintf ("btrfs",
  "enough disks for RAID 5: total %"
@@ -788,7 +823,7 @@ raid56_read_retry (struct grub_btrfs_data *data,
   if (chunk_type & GRUB_BTRFS_CHUNK_TYPE_RAID5)
 rebuild_raid5 (buf, buffers, nstripes, csize);
   else
-grub_dprintf ("btrfs", "called rebuild_raid6(), NOT IMPLEMENTED\n");
+rebuild_raid6 (buffers, nstripes, csize, parities_pos, buf, stripen);
 
   ret = GRUB_ERR_NONE;
  cleanup:
@@ -878,9 +913,11 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, 
grub_disk_addr_t addr,
unsigned redundancy = 1;
unsigned i, j;
int is_raid56;
+   grub_uint64_t parities_pos = 0;
 
-   is_raid56 = !!(grub_le_to_cpu64 (chunk->type) &
-  GRUB_BTRFS_CHUNK_TYPE_RAID5);
+is_raid56 = !!(grub_le_to_cpu64 (chunk->type) &
+  (GRUB_BTRFS_CHUNK_TYPE_RAID5 |
+   GRUB_BTRFS_CHUNK_TYPE_RAID6));
 
if (grub_le_to_cpu64 (chunk->size) <= off)
  {
@@ -1029,6 +1066,17 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, 
grub_disk_addr_t addr,
   */
  grub_divmod64 (high + stripen, nstripes, );
 
+ /*
+  * parities_pos is equal to "(high - nparities) % nstripes"
+  * (see the diagram above).
+  * However "high - nparities" can be negative, eg. when high
+  * == 0 leading to an incorrect computation.
+  * "high + nstripes - nparities" is always positive and in
+  * modulo nstripes is equal to "(high - nparities) % nstripes"
+  */
+ grub_divmod64 (high + nstripes - nparities, nstripes,
+_pos);
+
  stripe_offset = low + chunk_stripe_length * high;
  csize = chunk_stripe_length - low;
 
@@ -1069,7 +1117,7 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, 
grub_disk_addr_t addr,
grub_errno = GRUB_ERR_NONE;
if (err)
  err = raid56_read_retry (data, chunk, stripe_offset,
-  csize, buf);
+  stripen, csize, buf, parities_pos);
  }
else
  for (i = 0; i < redundancy; i++)
-- 
2.19.1


___
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel


[PATCH 1/9] btrfs: Add support for reading a filesystem with a RAID 5 or RAID 6 profile.

2018-10-18 Thread Goffredo Baroncelli
From: Goffredo Baroncelli 

Signed-off-by: Goffredo Baroncelli 
Signed-off-by: Daniel Kiper 
Reviewed-by: Daniel Kiper 
---
 grub-core/fs/btrfs.c | 73 
 1 file changed, 73 insertions(+)

diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
index be195448d..9122169aa 100644
--- a/grub-core/fs/btrfs.c
+++ b/grub-core/fs/btrfs.c
@@ -119,6 +119,8 @@ struct grub_btrfs_chunk_item
 #define GRUB_BTRFS_CHUNK_TYPE_RAID1 0x10
 #define GRUB_BTRFS_CHUNK_TYPE_DUPLICATED0x20
 #define GRUB_BTRFS_CHUNK_TYPE_RAID100x40
+#define GRUB_BTRFS_CHUNK_TYPE_RAID5 0x80
+#define GRUB_BTRFS_CHUNK_TYPE_RAID6 0x100
   grub_uint8_t dummy2[0xc];
   grub_uint16_t nstripes;
   grub_uint16_t nsubstripes;
@@ -764,6 +766,77 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, 
grub_disk_addr_t addr,
  stripe_offset = low + chunk_stripe_length
* high;
  csize = chunk_stripe_length - low;
+ break;
+   }
+ case GRUB_BTRFS_CHUNK_TYPE_RAID5:
+ case GRUB_BTRFS_CHUNK_TYPE_RAID6:
+   {
+ grub_uint64_t nparities, stripe_nr, high, low;
+
+ redundancy = 1;   /* no redundancy for now */
+
+ if (grub_le_to_cpu64 (chunk->type) & GRUB_BTRFS_CHUNK_TYPE_RAID5)
+   {
+ grub_dprintf ("btrfs", "RAID5\n");
+ nparities = 1;
+   }
+ else
+   {
+ grub_dprintf ("btrfs", "RAID6\n");
+ nparities = 2;
+   }
+
+ /*
+  * RAID 6 layout consists of several stripes spread over
+  * the disks, e.g.:
+  *
+  *   Disk_0  Disk_1  Disk_2  Disk_3
+  * A0  B0  P0  Q0
+  * Q1  A1  B1  P1
+  * P2  Q2  A2  B2
+  *
+  * Note: placement of the parities depend on row number.
+  *
+  * Pay attention that the btrfs terminology may differ from
+  * terminology used in other RAID implementations, e.g. LVM,
+  * dm or md. The main difference is that btrfs calls contiguous
+  * block of data on a given disk, e.g. A0, stripe instead of 
chunk.
+  *
+  * The variables listed below have following meaning:
+  *   - stripe_nr is the stripe number excluding the parities
+  * (A0 = 0, B0 = 1, A1 = 2, B1 = 3, etc.),
+  *   - high is the row number (0 for A0...Q0, 1 for Q1...P1, 
etc.),
+  *   - stripen is the disk number in a row (0 for A0, Q1, P2,
+  * 1 for B0, A1, Q2, etc.),
+  *   - off is the logical address to read,
+  *   - chunk_stripe_length is the size of a stripe (typically 64 
KiB),
+  *   - nstripes is the number of disks in a row,
+  *   - low is the offset of the data inside a stripe,
+  *   - stripe_offset is the data offset in an array,
+  *   - csize is the "potential" data to read; it will be reduced
+  * to size if the latter is smaller,
+  *   - nparities is the number of parities (1 for RAID 5, 2 for
+  * RAID 6); used only in RAID 5/6 code.
+  */
+ stripe_nr = grub_divmod64 (off, chunk_stripe_length, );
+
+ /*
+  * stripen is computed without the parities
+  * (0 for A0, A1, A2, 1 for B0, B1, B2, etc.).
+  */
+ high = grub_divmod64 (stripe_nr, nstripes - nparities, );
+
+ /*
+  * The stripes are spread over the disks. Every each row their
+  * positions are shifted by 1 place. So, the real disks number
+  * change. Hence, we have to take into account current row number
+  * modulo nstripes (0 for A0, 1 for A1, 2 for A2, etc.).
+  */
+ grub_divmod64 (high + stripen, nstripes, );
+
+ stripe_offset = low + chunk_stripe_length * high;
+ csize = chunk_stripe_length - low;
+
  break;
}
  default:
-- 
2.19.1


___
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel


[PATCH 3/9] btrfs: Move the error logging from find_device() to its caller.

2018-10-18 Thread Goffredo Baroncelli
From: Goffredo Baroncelli 

The caller knows better if this error is fatal or not, i.e. another disk is
available or not.

This is a preparatory patch.

Signed-off-by: Goffredo Baroncelli 
Reviewed-by: Daniel Kiper 
---
 grub-core/fs/btrfs.c | 10 --
 1 file changed, 4 insertions(+), 6 deletions(-)

diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
index 0cbf3551a..6b6e91cd1 100644
--- a/grub-core/fs/btrfs.c
+++ b/grub-core/fs/btrfs.c
@@ -603,12 +603,7 @@ find_device (struct grub_btrfs_data *data, grub_uint64_t 
id, int do_rescan)
   if (do_rescan)
 grub_device_iterate (find_device_iter, );
   if (!ctx.dev_found)
-{
-  grub_error (GRUB_ERR_BAD_FS,
- N_("couldn't find a necessary member device "
-"of multi-device filesystem"));
-  return NULL;
-}
+return NULL;
   data->n_devices_attached++;
   if (data->n_devices_attached > data->n_devices_allocated)
 {
@@ -905,6 +900,9 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, 
grub_disk_addr_t addr,
dev = find_device (data, stripe->device_id, j);
if (!dev)
  {
+   grub_dprintf ("btrfs",
+ "couldn't find a necessary member device "
+ "of multi-device filesystem\n");
err = grub_errno;
grub_errno = GRUB_ERR_NONE;
continue;
-- 
2.19.1


___
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel


Re: [PATCH 7/9] btrfs: Add support for recovery for a RAID 5 btrfs profiles.

2018-10-17 Thread Goffredo Baroncelli
On 17/10/2018 16.14, Daniel Kiper wrote:
> On Thu, Oct 11, 2018 at 08:51:01PM +0200, Goffredo Baroncelli wrote:
>> From: Goffredo Baroncelli 
>>
>> Add support for recovery for a RAID 5 btrfs profile. In addition
>> it is added some code as preparatory work for RAID 6 recovery code.
>>
>> Signed-off-by: Goffredo Baroncelli 
>> ---
[...]

>> +
>> +  for (failed_devices = 0, i = 0; i < nstripes; i++)
>> +{
>> +  struct grub_btrfs_chunk_stripe *stripe;
>> +  grub_disk_addr_t paddr;
>> +  grub_device_t dev;
>> +  grub_err_t err;
>> +
>> +  /* after the struct grub_btrfs_chunk_item, there is an array of
>> + struct grub_btrfs_chunk_stripe */
> 
> /* Struct grub_btrfs_chunk_stripe lives behind struct grub_btrfs_chunk_item. 
> */

What about 

/* The struct grub_btrfs_chunk_stripe array lives behind struct 
grub_btrfs_chunk_item. */

[...]

>> @@ -921,17 +1061,29 @@ grub_btrfs_read_logical (struct grub_btrfs_data 
>> *data, grub_disk_addr_t addr,
>>  grub_dprintf ("btrfs", "reading laddr 0x%" PRIxGRUB_UINT64_T "\n",
>>addr);
>>
>> -for (i = 0; i < redundancy; i++)
>> +if (!is_raid56)
> 
> Why not "if (is_raid56)"? I asked about that earlier. Please change
> this if and of course code below. It will be much easier to read. And
> you do not need curly brackets for for loop after else.

Frankly speaking I don't see any problem having a if (!...). However I update 
the code as your request, hoping to speedup this patch set

[...]


-- 
gpg @keyserver.linux.it: Goffredo Baroncelli 
Key fingerprint BBF5 1610 0B64 DAC6 5F7D  17B2 0EDA 9B37 8B82 E0B5

___
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel


[PATCH 9/9] btrfs: Add RAID 6 recovery for a btrfs filesystem.

2018-10-11 Thread Goffredo Baroncelli
From: Goffredo Baroncelli 

Add the RAID 6 recovery, in order to use a RAID 6 filesystem even if some
disks (up to two) are missing. This code use the md RAID 6 code already
present in grub.

Signed-off-by: Goffredo Baroncelli 
Reviewed-by: Daniel Kiper 
---
 grub-core/fs/btrfs.c | 60 +++-
 1 file changed, 54 insertions(+), 6 deletions(-)

diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
index d066d54cc..d20ee09e4 100644
--- a/grub-core/fs/btrfs.c
+++ b/grub-core/fs/btrfs.c
@@ -30,6 +30,7 @@
 #include 
 #include 
 #include 
+#include 
 
 GRUB_MOD_LICENSE ("GPLv3+");
 
@@ -702,11 +703,36 @@ rebuild_raid5 (char *dest, struct raid56_buffer *buffers,
 }
 }
 
+static grub_err_t
+raid6_recover_read_buffer (void *data, int disk_nr,
+  grub_uint64_t addr __attribute__ ((unused)),
+  void *dest, grub_size_t size)
+{
+struct raid56_buffer *buffers = data;
+
+if (!buffers[disk_nr].data_is_valid)
+   return grub_errno = GRUB_ERR_READ_ERROR;
+
+grub_memcpy(dest, buffers[disk_nr].buf, size);
+
+return grub_errno = GRUB_ERR_NONE;
+}
+
+static void
+rebuild_raid6 (struct raid56_buffer *buffers, grub_uint64_t nstripes,
+   grub_uint64_t csize, grub_uint64_t parities_pos, void *dest,
+   grub_uint64_t stripen)
+
+{
+  grub_raid6_recover_gen (buffers, nstripes, stripen, parities_pos,
+  dest, 0, csize, 0, raid6_recover_read_buffer);
+}
+
 static grub_err_t
 raid56_read_retry (struct grub_btrfs_data *data,
   struct grub_btrfs_chunk_item *chunk,
-  grub_uint64_t stripe_offset,
-  grub_uint64_t csize, void *buf)
+  grub_uint64_t stripe_offset, grub_uint64_t stripen,
+  grub_uint64_t csize, void *buf, grub_uint64_t parities_pos)
 {
   struct raid56_buffer *buffers;
   grub_uint64_t nstripes = grub_le_to_cpu16 (chunk->nstripes);
@@ -779,6 +805,15 @@ raid56_read_retry (struct grub_btrfs_data *data,
   ret = GRUB_ERR_READ_ERROR;
   goto cleanup;
 }
+  else if (failed_devices > 2 && (chunk_type & GRUB_BTRFS_CHUNK_TYPE_RAID6))
+{
+  grub_dprintf ("btrfs",
+   "not enough disks for raid6: total %" PRIuGRUB_UINT64_T
+   ", missing %" PRIuGRUB_UINT64_T "\n",
+   nstripes, failed_devices);
+  ret = GRUB_ERR_READ_ERROR;
+  goto cleanup;
+}
   else
 grub_dprintf ("btrfs",
  "enough disks for RAID 5 rebuilding: total %"
@@ -789,7 +824,7 @@ raid56_read_retry (struct grub_btrfs_data *data,
   if (chunk_type & GRUB_BTRFS_CHUNK_TYPE_RAID5)
 rebuild_raid5 (buf, buffers, nstripes, csize);
   else
-grub_dprintf ("btrfs", "called rebuild_raid6(), NOT IMPLEMENTED\n");
+rebuild_raid6 (buffers, nstripes, csize, parities_pos, buf, stripen);
 
   ret = GRUB_ERR_NONE;
  cleanup:
@@ -879,9 +914,11 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, 
grub_disk_addr_t addr,
unsigned redundancy = 1;
unsigned i, j;
int is_raid56;
+   grub_uint64_t parities_pos = 0;
 
-   is_raid56 = !!(grub_le_to_cpu64 (chunk->type) &
-  GRUB_BTRFS_CHUNK_TYPE_RAID5);
+is_raid56 = !!(grub_le_to_cpu64 (chunk->type) &
+  (GRUB_BTRFS_CHUNK_TYPE_RAID5 |
+   GRUB_BTRFS_CHUNK_TYPE_RAID6));
 
if (grub_le_to_cpu64 (chunk->size) <= off)
  {
@@ -1030,6 +1067,17 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, 
grub_disk_addr_t addr,
   */
  grub_divmod64 (high + stripen, nstripes, );
 
+ /*
+  * parities_pos is equal to "(high - nparities) % nstripes"
+  * (see the diagram above).
+  * However "high - nparities" might be negative (eg when high
+  * == 0) leading to an incorrect computation.
+  * Instead "high + nstripes - nparities" is always positive and
+  * in modulo nstripes is equal to "(high - nparities) % nstripes"
+  */
+ grub_divmod64 (high + nstripes - nparities, nstripes,
+_pos);
+
  stripe_offset = low + chunk_stripe_length * high;
  csize = chunk_stripe_length - low;
 
@@ -1081,7 +1129,7 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, 
grub_disk_addr_t addr,
grub_errno = GRUB_ERR_NONE;
if (err)
  err = raid56_read_retry (data, chunk, stripe_offset,
-  csize, buf);
+  stripen, csize, buf, parities_pos);
  }
if (!err)
  break;
-- 
2.19.1


___
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel


[PATCH 6/9] btrfs: Refactor the code that read from disk

2018-10-11 Thread Goffredo Baroncelli
From: Goffredo Baroncelli 

Move the code in charge to read the data from disk into a separate
function. This helps to separate the error handling logic (which depends on
the different raid profiles) from the read from disk logic.
Refactoring this code increases the general readability too.

This is a preparatory patch, to help the adding of the RAID 5/6 recovery
code.

Signed-off-by: Goffredo Baroncelli 
Reviewed-by: Daniel Kiper 
---
 grub-core/fs/btrfs.c | 75 ++--
 1 file changed, 44 insertions(+), 31 deletions(-)

diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
index a82211ccc..899dc32b7 100644
--- a/grub-core/fs/btrfs.c
+++ b/grub-core/fs/btrfs.c
@@ -625,6 +625,46 @@ find_device (struct grub_btrfs_data *data, grub_uint64_t 
id)
   return ctx.dev_found;
 }
 
+static grub_err_t
+btrfs_read_from_chunk (struct grub_btrfs_data *data,
+  struct grub_btrfs_chunk_item *chunk,
+  grub_uint64_t stripen, grub_uint64_t stripe_offset,
+  int redundancy, grub_uint64_t csize,
+  void *buf)
+{
+struct grub_btrfs_chunk_stripe *stripe;
+grub_disk_addr_t paddr;
+grub_device_t dev;
+grub_err_t err;
+
+stripe = (struct grub_btrfs_chunk_stripe *) (chunk + 1);
+/* Right now the redundancy handling is easy.
+   With RAID5-like it will be more difficult.  */
+stripe += stripen + redundancy;
+
+paddr = grub_le_to_cpu64 (stripe->offset) + stripe_offset;
+
+grub_dprintf ("btrfs", "stripe %" PRIxGRUB_UINT64_T
+ " maps to 0x%" PRIxGRUB_UINT64_T
+ ". Reading paddr 0x%" PRIxGRUB_UINT64_T "\n",
+ stripen, stripe->offset, paddr);
+
+dev = find_device (data, stripe->device_id);
+if (!dev)
+  {
+   grub_dprintf ("btrfs",
+ "couldn't find a necessary member device "
+ "of multi-device filesystem\n");
+   grub_errno = GRUB_ERR_NONE;
+   return GRUB_ERR_READ_ERROR;
+  }
+
+err = grub_disk_read (dev->disk, paddr >> GRUB_DISK_SECTOR_BITS,
+ paddr & (GRUB_DISK_SECTOR_SIZE - 1),
+ csize, buf);
+return err;
+}
+
 static grub_err_t
 grub_btrfs_read_logical (struct grub_btrfs_data *data, grub_disk_addr_t addr,
 void *buf, grub_size_t size, int recursion_depth)
@@ -638,7 +678,6 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, 
grub_disk_addr_t addr,
   grub_err_t err = 0;
   struct grub_btrfs_key key_out;
   int challoc = 0;
-  grub_device_t dev;
   struct grub_btrfs_key key_in;
   grub_size_t chsize;
   grub_disk_addr_t chaddr;
@@ -884,36 +923,10 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, 
grub_disk_addr_t addr,
 
for (i = 0; i < redundancy; i++)
  {
-   struct grub_btrfs_chunk_stripe *stripe;
-   grub_disk_addr_t paddr;
-
-   stripe = (struct grub_btrfs_chunk_stripe *) (chunk + 1);
-   /* Right now the redundancy handling is easy.
-  With RAID5-like it will be more difficult.  */
-   stripe += stripen + i;
-
-   paddr = grub_le_to_cpu64 (stripe->offset) + stripe_offset;
-
-   grub_dprintf ("btrfs", "stripe %" PRIxGRUB_UINT64_T
- " maps to 0x%" PRIxGRUB_UINT64_T "\n",
- stripen, stripe->offset);
-   grub_dprintf ("btrfs", "reading paddr 0x%" PRIxGRUB_UINT64_T
- "\n", paddr);
-
-   dev = find_device (data, stripe->device_id);
-   if (!dev)
- {
-   grub_dprintf ("btrfs",
- "couldn't find a necessary member device "
- "of multi-device filesystem\n");
-   err = grub_errno;
-   grub_errno = GRUB_ERR_NONE;
-   continue;
- }
-
-   err = grub_disk_read (dev->disk, paddr >> GRUB_DISK_SECTOR_BITS,
- paddr & (GRUB_DISK_SECTOR_SIZE - 1),
- csize, buf);
+   err = btrfs_read_from_chunk (data, chunk, stripen,
+stripe_offset,
+i, /* redundancy */
+csize, buf);
if (!err)
  break;
grub_errno = GRUB_ERR_NONE;
-- 
2.19.1


___
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel


[PATCH 5/9] btrfs: Move logging code in grub_btrfs_read_logical()

2018-10-11 Thread Goffredo Baroncelli
From: Goffredo Baroncelli 

A portion of the logging code is moved outside of internal for(;;). The part
that is left inside is the one which depends on the internal for(;;) index.

This is a preparatory patch. The next one will refactor the code inside
the for(;;) into an another function.

Signed-off-by: Goffredo Baroncelli 
Reviewed-by: Daniel Kiper 
---
 grub-core/fs/btrfs.c | 25 ++---
 1 file changed, 14 insertions(+), 11 deletions(-)

diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
index b2be80c33..a82211ccc 100644
--- a/grub-core/fs/btrfs.c
+++ b/grub-core/fs/btrfs.c
@@ -870,6 +870,18 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, 
grub_disk_addr_t addr,
 
for (j = 0; j < 2; j++)
  {
+   grub_dprintf ("btrfs", "chunk 0x%" PRIxGRUB_UINT64_T
+ "+0x%" PRIxGRUB_UINT64_T
+ " (%d stripes (%d substripes) of %"
+ PRIxGRUB_UINT64_T ")\n",
+ grub_le_to_cpu64 (key->offset),
+ grub_le_to_cpu64 (chunk->size),
+ grub_le_to_cpu16 (chunk->nstripes),
+ grub_le_to_cpu16 (chunk->nsubstripes),
+ grub_le_to_cpu64 (chunk->stripe_length));
+   grub_dprintf ("btrfs", "reading laddr 0x%" PRIxGRUB_UINT64_T "\n",
+ addr);
+
for (i = 0; i < redundancy; i++)
  {
struct grub_btrfs_chunk_stripe *stripe;
@@ -882,20 +894,11 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, 
grub_disk_addr_t addr,
 
paddr = grub_le_to_cpu64 (stripe->offset) + stripe_offset;
 
-   grub_dprintf ("btrfs", "chunk 0x%" PRIxGRUB_UINT64_T
- "+0x%" PRIxGRUB_UINT64_T
- " (%d stripes (%d substripes) of %"
- PRIxGRUB_UINT64_T ") stripe %" PRIxGRUB_UINT64_T
+   grub_dprintf ("btrfs", "stripe %" PRIxGRUB_UINT64_T
  " maps to 0x%" PRIxGRUB_UINT64_T "\n",
- grub_le_to_cpu64 (key->offset),
- grub_le_to_cpu64 (chunk->size),
- grub_le_to_cpu16 (chunk->nstripes),
- grub_le_to_cpu16 (chunk->nsubstripes),
- grub_le_to_cpu64 (chunk->stripe_length),
  stripen, stripe->offset);
grub_dprintf ("btrfs", "reading paddr 0x%" PRIxGRUB_UINT64_T
- " for laddr 0x%" PRIxGRUB_UINT64_T "\n", paddr,
- addr);
+ "\n", paddr);
 
dev = find_device (data, stripe->device_id);
if (!dev)
-- 
2.19.1


___
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel


[PATCH V9] Add support for BTRFS raid5/6 to GRUB

2018-10-11 Thread Goffredo Baroncelli


Hi All,

the aim of this patches set is to provide support for a BTRFS raid5/6
filesystem in GRUB.

The first patch, implements the basic support for raid5/6. I.e this works when
all the disks are present.

The next 5 patches, are preparatory ones.

The 7th patch implements the raid5 recovery for btrfs (i.e. handling the
disappearing of 1 disk).
The 8th patch makes the code for handling the raid6 recovery more generic.
The last one implements the raid6 recovery for btrfs (i.e. handling the
disappearing up to two disks).

I tested the code in grub-emu, and it works both with all the disks,
and with some disks missing. I checked the crc32 calculated from grub and
from linux and these matched. Finally I checked if the support for md raid6
still works properly, and it does (with all drives and with up to 2 drives
missing)

Comments are welcome.

Changelog
v1: initial support for btrfs raid5/6. No recovery allowed
v2: full support for btrfs raid5/6. Recovery allowed
v3: some minor cleanup suggested by Daniel Kiper; reusing the
original raid6 recovery code of grub
v4: Several spell fix; better description of the RAID layout
in btrfs, and the variables which describes the stripe
positioning; split the patch #5 in two (#5 and #6)
v5: Several spell fix; improved code comment in patch #1, small
clean up in the code
v6: Small cleanup; improved the wording in the RAID6 layout
description; in the function raid6_recover_read_buffer() avoid
a unnecessary memcpy in case of invalid data;
v7: - patch 2,3,5,6,8 received an Review-by Daniel, and were unchanged from
the last time (only minor cleanup in the commit description requested by
Daniel)
- patch 7 received some small update rearranging a for(), and some
bracket around if()
- patch 4, received an update message which explains better why NULL
is stored in data->devices_attached[]
- patch 9, received a blank line to separate better a code line from
a previous comment. A description of 'parities_pos' was added
- patch 1, received a major update about the variable meaning description
in the comment. However I suspect that we need some further review to reach
a fully agreement about this text. NB: the update are relate only to 
comments
v8: - patch 2,5,6,8 received an Review-by Daniel, and were unchanged from
the last time (only minor cleanup in the commit description requested by
Daniel)
- patch 1 received some adjustement to the variables description due to
  the different terminology between BTRFS and other RAID implementatio.
  Added a description for the "nparities" variable.
- patch 3 removed some unnecessary curly brackets (change request by Daniel)
- patch 4 received an improved commit description about why and how 
  the function find_device() is changed
- patch 7 received an update which transforms a i = 0; while(i..) i++; in
  for( i = 0. ; i++);
- patch 9 received an update to the comment
v9: - patch 1: update comments
- patch 4: update commit messages
- patch 7: added a comment about accessing an array of structs
  after another structs; changed if(err == GRUB_ERR_NONE) in if(!err)
  changed if(err != GRUB_ERR_NONE) in if(err)



BR
G.Baroncelli

--
gpg @keyserver.linux.it: Goffredo Baroncelli 
Key fingerprint BBF5 1610 0B64 DAC6 5F7D  17B2 0EDA 9B37 8B82 E0B5






___
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel


[PATCH 8/9] btrfs: Make more generic the code for RAID 6 rebuilding

2018-10-11 Thread Goffredo Baroncelli
From: Goffredo Baroncelli 

The original code which handles the recovery of a RAID 6 disks array
assumes that all reads are multiple of 1 << GRUB_DISK_SECTOR_BITS and it
assumes that all the I/O is done via the struct grub_diskfilter_segment.
This is not true for the btrfs code. In order to reuse the native
grub_raid6_recover() code, it is modified to not call
grub_diskfilter_read_node() directly, but to call an handler passed
as an argument.

Signed-off-by: Goffredo Baroncelli 
Reviewed-by: Daniel Kiper 
---
 grub-core/disk/raid6_recover.c | 52 ++
 include/grub/diskfilter.h  |  9 ++
 2 files changed, 43 insertions(+), 18 deletions(-)

diff --git a/grub-core/disk/raid6_recover.c b/grub-core/disk/raid6_recover.c
index aa674f6ca..0cf691ddf 100644
--- a/grub-core/disk/raid6_recover.c
+++ b/grub-core/disk/raid6_recover.c
@@ -74,14 +74,26 @@ mod_255 (unsigned x)
 }
 
 static grub_err_t
-grub_raid6_recover (struct grub_diskfilter_segment *array, int disknr, int p,
-char *buf, grub_disk_addr_t sector, grub_size_t size)
+raid6_recover_read_node (void *data, int disknr,
+   grub_uint64_t sector,
+   void *buf, grub_size_t size)
+{
+struct grub_diskfilter_segment *array = data;
+
+return grub_diskfilter_read_node (>nodes[disknr],
+ (grub_disk_addr_t)sector,
+ size >> GRUB_DISK_SECTOR_BITS, buf);
+}
+
+grub_err_t
+grub_raid6_recover_gen (void *data, grub_uint64_t nstripes, int disknr, int p,
+   char *buf, grub_uint64_t sector, grub_size_t size,
+   int layout, raid_recover_read_t read_func)
 {
   int i, q, pos;
   int bad1 = -1, bad2 = -1;
   char *pbuf = 0, *qbuf = 0;
 
-  size <<= GRUB_DISK_SECTOR_BITS;
   pbuf = grub_zalloc (size);
   if (!pbuf)
 goto quit;
@@ -91,17 +103,17 @@ grub_raid6_recover (struct grub_diskfilter_segment *array, 
int disknr, int p,
 goto quit;
 
   q = p + 1;
-  if (q == (int) array->node_count)
+  if (q == (int) nstripes)
 q = 0;
 
   pos = q + 1;
-  if (pos == (int) array->node_count)
+  if (pos == (int) nstripes)
 pos = 0;
 
-  for (i = 0; i < (int) array->node_count - 2; i++)
+  for (i = 0; i < (int) nstripes - 2; i++)
 {
   int c;
-  if (array->layout & GRUB_RAID_LAYOUT_MUL_FROM_POS)
+  if (layout & GRUB_RAID_LAYOUT_MUL_FROM_POS)
c = pos;
   else
c = i;
@@ -109,8 +121,7 @@ grub_raid6_recover (struct grub_diskfilter_segment *array, 
int disknr, int p,
 bad1 = c;
   else
 {
-  if (! grub_diskfilter_read_node (>nodes[pos], sector,
-  size >> GRUB_DISK_SECTOR_BITS, buf))
+ if (!read_func(data, pos, sector, buf, size))
 {
   grub_crypto_xor (pbuf, pbuf, buf, size);
   grub_raid_block_mulx (c, buf, size);
@@ -128,7 +139,7 @@ grub_raid6_recover (struct grub_diskfilter_segment *array, 
int disknr, int p,
 }
 
   pos++;
-  if (pos == (int) array->node_count)
+  if (pos == (int) nstripes)
 pos = 0;
 }
 
@@ -139,16 +150,14 @@ grub_raid6_recover (struct grub_diskfilter_segment 
*array, int disknr, int p,
   if (bad2 < 0)
 {
   /* One bad device */
-  if ((! grub_diskfilter_read_node (>nodes[p], sector,
-   size >> GRUB_DISK_SECTOR_BITS, buf)))
+  if (!read_func(data, p, sector, buf, size))
 {
   grub_crypto_xor (buf, buf, pbuf, size);
   goto quit;
 }
 
   grub_errno = GRUB_ERR_NONE;
-  if (grub_diskfilter_read_node (>nodes[q], sector,
-size >> GRUB_DISK_SECTOR_BITS, buf))
+  if (read_func(data, q, sector, buf, size))
 goto quit;
 
   grub_crypto_xor (buf, buf, qbuf, size);
@@ -160,14 +169,12 @@ grub_raid6_recover (struct grub_diskfilter_segment 
*array, int disknr, int p,
   /* Two bad devices */
   unsigned c;
 
-  if (grub_diskfilter_read_node (>nodes[p], sector,
-size >> GRUB_DISK_SECTOR_BITS, buf))
+  if (read_func(data, p, sector, buf, size))
 goto quit;
 
   grub_crypto_xor (pbuf, pbuf, buf, size);
 
-  if (grub_diskfilter_read_node (>nodes[q], sector,
-size >> GRUB_DISK_SECTOR_BITS, buf))
+  if (read_func(data, q, sector, buf, size))
 goto quit;
 
   grub_crypto_xor (qbuf, qbuf, buf, size);
@@ -190,6 +197,15 @@ quit:
   return grub_errno;
 }
 
+static grub_err_t
+grub_raid6_recover (struct grub_diskfilter_segment *array, int disknr, int p,
+char *buf, grub_disk_addr_t sector, grub_size_t size)
+{
+  return grub_raid6_recover_gen (array, array->n

[PATCH 7/9] btrfs: Add support for recovery for a RAID 5 btrfs profiles.

2018-10-11 Thread Goffredo Baroncelli
From: Goffredo Baroncelli 

Add support for recovery for a RAID 5 btrfs profile. In addition
it is added some code as preparatory work for RAID 6 recovery code.

Signed-off-by: Goffredo Baroncelli 
---
 grub-core/fs/btrfs.c | 162 +--
 1 file changed, 157 insertions(+), 5 deletions(-)

diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
index 899dc32b7..d066d54cc 100644
--- a/grub-core/fs/btrfs.c
+++ b/grub-core/fs/btrfs.c
@@ -29,6 +29,7 @@
 #include 
 #include 
 #include 
+#include 
 
 GRUB_MOD_LICENSE ("GPLv3+");
 
@@ -665,6 +666,141 @@ btrfs_read_from_chunk (struct grub_btrfs_data *data,
 return err;
 }
 
+struct raid56_buffer {
+  void *buf;
+  int  data_is_valid;
+};
+
+static void
+rebuild_raid5 (char *dest, struct raid56_buffer *buffers,
+  grub_uint64_t nstripes, grub_uint64_t csize)
+{
+  grub_uint64_t i;
+  int first;
+
+  for(i = 0; buffers[i].data_is_valid && i < nstripes; i++);
+
+  if (i == nstripes)
+{
+  grub_dprintf ("btrfs", "called rebuild_raid5(), but all disks are OK\n");
+  return;
+}
+
+  grub_dprintf ("btrfs", "rebuilding RAID 5 stripe #%" PRIuGRUB_UINT64_T "\n", 
i);
+
+  for (i = 0, first = 1; i < nstripes; i++)
+{
+  if (!buffers[i].data_is_valid)
+   continue;
+
+  if (first) {
+   grub_memcpy(dest, buffers[i].buf, csize);
+   first = 0;
+  } else
+   grub_crypto_xor (dest, dest, buffers[i].buf, csize);
+
+}
+}
+
+static grub_err_t
+raid56_read_retry (struct grub_btrfs_data *data,
+  struct grub_btrfs_chunk_item *chunk,
+  grub_uint64_t stripe_offset,
+  grub_uint64_t csize, void *buf)
+{
+  struct raid56_buffer *buffers;
+  grub_uint64_t nstripes = grub_le_to_cpu16 (chunk->nstripes);
+  grub_uint64_t chunk_type = grub_le_to_cpu64 (chunk->type);
+  grub_err_t ret = GRUB_ERR_OUT_OF_MEMORY;
+  grub_uint64_t i, failed_devices;
+
+  buffers = grub_zalloc (sizeof(*buffers) * nstripes);
+  if (!buffers)
+goto cleanup;
+
+  for (i = 0; i < nstripes; i++)
+{
+  buffers[i].buf = grub_zalloc (csize);
+  if (!buffers[i].buf)
+   goto cleanup;
+}
+
+  for (failed_devices = 0, i = 0; i < nstripes; i++)
+{
+  struct grub_btrfs_chunk_stripe *stripe;
+  grub_disk_addr_t paddr;
+  grub_device_t dev;
+  grub_err_t err;
+
+  /* after the struct grub_btrfs_chunk_item, there is an array of
+ struct grub_btrfs_chunk_stripe */
+  stripe = (struct grub_btrfs_chunk_stripe *) (chunk + 1) + i;
+
+  paddr = grub_le_to_cpu64 (stripe->offset) + stripe_offset;
+  grub_dprintf ("btrfs", "reading paddr %" PRIxGRUB_UINT64_T
+" from stripe ID %" PRIxGRUB_UINT64_T "\n", paddr,
+stripe->device_id);
+
+  dev = find_device (data, stripe->device_id);
+  if (!dev)
+   {
+ buffers[i].data_is_valid = 0;
+ grub_dprintf ("btrfs", "stripe %" PRIuGRUB_UINT64_T " FAILED (dev ID 
%"
+   PRIxGRUB_UINT64_T ")\n", i, stripe->device_id);
+ failed_devices++;
+ continue;
+   }
+
+  err = grub_disk_read (dev->disk, paddr >> GRUB_DISK_SECTOR_BITS,
+   paddr & (GRUB_DISK_SECTOR_SIZE - 1),
+   csize, buffers[i].buf);
+  if (err == GRUB_ERR_NONE)
+   {
+ buffers[i].data_is_valid = 1;
+ grub_dprintf ("btrfs", "stripe %" PRIuGRUB_UINT64_T " Ok (dev ID %"
+   PRIxGRUB_UINT64_T ")\n", i, stripe->device_id);
+   }
+  else
+   {
+ buffers[i].data_is_valid = 0;
+ grub_dprintf ("btrfs", "stripe %" PRIuGRUB_UINT64_T
+   " FAILED (dev ID %" PRIxGRUB_UINT64_T ")\n", i,
+   stripe->device_id);
+ failed_devices++;
+   }
+}
+
+  if (failed_devices > 1 && (chunk_type & GRUB_BTRFS_CHUNK_TYPE_RAID5))
+{
+  grub_dprintf ("btrfs",
+   "not enough disks for RAID 5: total %" PRIuGRUB_UINT64_T
+   ", missing %" PRIuGRUB_UINT64_T "\n",
+   nstripes, failed_devices);
+  ret = GRUB_ERR_READ_ERROR;
+  goto cleanup;
+}
+  else
+grub_dprintf ("btrfs",
+ "enough disks for RAID 5 rebuilding: total %"
+ PRIuGRUB_UINT64_T ", missing %" PRIuGRUB_UINT64_T "\n",
+ nstripes, failed_devices);
+
+  /* if these are enough, try to rebuild the data */
+  if (chunk_type & GRUB_BTRFS_CHUNK_TYPE_RAID5)
+rebuild_raid5 (buf, buffers, nstripes, csize);
+  else
+grub_dprintf (&qu

[PATCH 1/9] btrfs: Add support for reading a filesystem with a RAID 5 or RAID 6 profile.

2018-10-11 Thread Goffredo Baroncelli
From: Goffredo Baroncelli 

Signed-off-by: Goffredo Baroncelli 
Signed-off-by: Daniel Kiper 
---
 grub-core/fs/btrfs.c | 73 
 1 file changed, 73 insertions(+)

diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
index be195448d..933a57d3b 100644
--- a/grub-core/fs/btrfs.c
+++ b/grub-core/fs/btrfs.c
@@ -119,6 +119,8 @@ struct grub_btrfs_chunk_item
 #define GRUB_BTRFS_CHUNK_TYPE_RAID1 0x10
 #define GRUB_BTRFS_CHUNK_TYPE_DUPLICATED0x20
 #define GRUB_BTRFS_CHUNK_TYPE_RAID100x40
+#define GRUB_BTRFS_CHUNK_TYPE_RAID5 0x80
+#define GRUB_BTRFS_CHUNK_TYPE_RAID6 0x100
   grub_uint8_t dummy2[0xc];
   grub_uint16_t nstripes;
   grub_uint16_t nsubstripes;
@@ -764,6 +766,77 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, 
grub_disk_addr_t addr,
  stripe_offset = low + chunk_stripe_length
* high;
  csize = chunk_stripe_length - low;
+ break;
+   }
+ case GRUB_BTRFS_CHUNK_TYPE_RAID5:
+ case GRUB_BTRFS_CHUNK_TYPE_RAID6:
+   {
+ grub_uint64_t nparities, stripe_nr, high, low;
+
+ redundancy = 1;   /* no redundancy for now */
+
+ if (grub_le_to_cpu64 (chunk->type) & GRUB_BTRFS_CHUNK_TYPE_RAID5)
+   {
+ grub_dprintf ("btrfs", "RAID5\n");
+ nparities = 1;
+   }
+ else
+   {
+ grub_dprintf ("btrfs", "RAID6\n");
+ nparities = 2;
+   }
+
+ /*
+  * RAID 6 layout consists of several stripes spread over
+  * the disks, e.g.:
+  *
+  *   Disk_0  Disk_1  Disk_2  Disk_3
+  * A0  B0  P0  Q0
+  * Q1  A1  B1  P1
+  * P2  Q2  A2  B2
+  *
+  * Note: placement of the parities depend on row number.
+  *
+  * Pay attention that the btrfs terminology may differ from
+  * terminology used in other RAID implementations, e.g. LVM,
+  * dm or md. The main difference is that btrfs calls contiguous
+  * block of data on a given disk, e.g. A0, stripe instead of 
chunk.
+  *
+  * The variables listed below have following meaning:
+  *   - stripe_nr is the stripe number excluding the parities
+  * (A0 = 0, B0 = 1, A1 = 2, B1 = 3, etc.),
+  *   - high is the row number (0 for A0...Q0, 1 for Q1...P1, 
etc.),
+  *   - stripen is the disk number in a row (0 for A0, Q1, P2,
+  * 1 for B0, A1, Q2, etc.),
+  *   - off is the logical address to read,
+  *   - chunk_stripe_length is the size of a stripe (typically 64 
KiB),
+  *   - nstripes is the number of disks in a row,
+  *   - low is the offset of the data inside a stripe,
+  *   - stripe_offset is the data offset in an array,
+  *   - csize is the "potential" data to read; it will be reduced
+  * to size if the latter is smaller,
+  *   - nparities is the number of parities (1 for RAID 5, 2 for
+  * RAID 6); used only in RAID 5/6 code.
+  */
+ stripe_nr = grub_divmod64 (off, chunk_stripe_length, );
+
+ /*
+  * stripen is computed without the parities
+  * (0 for A0, A1, A2, 1 for B0, B1, B2, etc.).
+  */
+ high = grub_divmod64 (stripe_nr, nstripes - nparities, );
+
+ /*
+  * The stripes are spread over the disks. Every each row their
+  * positions are shifted by 1 place. So, the real disks number
+  * change. Hence, we have to take current row number modulo
+  * nstripes into account (0 for A0, 1 for A1, 2 for A2, etc.).
+  */
+ grub_divmod64 (high + stripen, nstripes, );
+
+ stripe_offset = low + chunk_stripe_length * high;
+ csize = chunk_stripe_length - low;
+
  break;
}
  default:
-- 
2.19.1


___
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel


[PATCH 4/9] btrfs: Avoid a rescan for a device which was already not found.

2018-10-11 Thread Goffredo Baroncelli
From: Goffredo Baroncelli 

Currently read from missing device triggers rescan. However, it is never
recorded that the device is missing. So, each read of a missing device
triggers rescan again and again. This behavior causes a lot of unneeded
rescans leading to huge slowdowns.

This patch fixes above mentioned issue. Information about missing devices
is stored in the data->devices_attached[] array as NULL value in dev
member. Rescan is triggered only if no information is found for a given
device. This means that only first time read triggers rescan.

The patch drops premature return. This way data->devices_attached[] is
filled even when a given device is missing.

Signed-off-by: Goffredo Baroncelli 
---
 grub-core/fs/btrfs.c | 17 +
 1 file changed, 9 insertions(+), 8 deletions(-)

diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
index 2a87eb103..b2be80c33 100644
--- a/grub-core/fs/btrfs.c
+++ b/grub-core/fs/btrfs.c
@@ -588,7 +588,7 @@ find_device_iter (const char *name, void *data)
 }
 
 static grub_device_t
-find_device (struct grub_btrfs_data *data, grub_uint64_t id, int do_rescan)
+find_device (struct grub_btrfs_data *data, grub_uint64_t id)
 {
   struct find_device_ctx ctx = {
 .data = data,
@@ -600,10 +600,9 @@ find_device (struct grub_btrfs_data *data, grub_uint64_t 
id, int do_rescan)
   for (i = 0; i < data->n_devices_attached; i++)
 if (id == data->devices_attached[i].id)
   return data->devices_attached[i].dev;
-  if (do_rescan)
-grub_device_iterate (find_device_iter, );
-  if (!ctx.dev_found)
-return NULL;
+
+  grub_device_iterate (find_device_iter, );
+
   data->n_devices_attached++;
   if (data->n_devices_attached > data->n_devices_allocated)
 {
@@ -615,7 +614,8 @@ find_device (struct grub_btrfs_data *data, grub_uint64_t 
id, int do_rescan)
* sizeof (data->devices_attached[0]));
   if (!data->devices_attached)
{
- grub_device_close (ctx.dev_found);
+ if (ctx.dev_found)
+   grub_device_close (ctx.dev_found);
  data->devices_attached = tmp;
  return NULL;
}
@@ -897,7 +897,7 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, 
grub_disk_addr_t addr,
  " for laddr 0x%" PRIxGRUB_UINT64_T "\n", paddr,
  addr);
 
-   dev = find_device (data, stripe->device_id, j);
+   dev = find_device (data, stripe->device_id);
if (!dev)
  {
grub_dprintf ("btrfs",
@@ -974,7 +974,8 @@ grub_btrfs_unmount (struct grub_btrfs_data *data)
   unsigned i;
   /* The device 0 is closed one layer upper.  */
   for (i = 1; i < data->n_devices_attached; i++)
-grub_device_close (data->devices_attached[i].dev);
+if (data->devices_attached[i].dev)
+grub_device_close (data->devices_attached[i].dev);
   grub_free (data->devices_attached);
   grub_free (data->extent);
   grub_free (data);
-- 
2.19.1


___
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel


[PATCH 3/9] btrfs: Move the error logging from find_device() to its caller.

2018-10-11 Thread Goffredo Baroncelli
From: Goffredo Baroncelli 

The caller knows better if this error is fatal or not, i.e. another disk is
available or not.

This is a preparatory patch.

Signed-off-by: Goffredo Baroncelli 
Reviewed-by: Daniel Kiper 
---
 grub-core/fs/btrfs.c | 10 --
 1 file changed, 4 insertions(+), 6 deletions(-)

diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
index c9f0c4193..2a87eb103 100644
--- a/grub-core/fs/btrfs.c
+++ b/grub-core/fs/btrfs.c
@@ -603,12 +603,7 @@ find_device (struct grub_btrfs_data *data, grub_uint64_t 
id, int do_rescan)
   if (do_rescan)
 grub_device_iterate (find_device_iter, );
   if (!ctx.dev_found)
-{
-  grub_error (GRUB_ERR_BAD_FS,
- N_("couldn't find a necessary member device "
-"of multi-device filesystem"));
-  return NULL;
-}
+return NULL;
   data->n_devices_attached++;
   if (data->n_devices_attached > data->n_devices_allocated)
 {
@@ -905,6 +900,9 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, 
grub_disk_addr_t addr,
dev = find_device (data, stripe->device_id, j);
if (!dev)
  {
+   grub_dprintf ("btrfs",
+ "couldn't find a necessary member device "
+ "of multi-device filesystem\n");
err = grub_errno;
grub_errno = GRUB_ERR_NONE;
continue;
-- 
2.19.1


___
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel


[PATCH 2/9] btrfs: Add helper to check the btrfs header.

2018-10-11 Thread Goffredo Baroncelli
From: Goffredo Baroncelli 

This helper is used in a few places to help the debugging. As
conservative approach the error is only logged.
This does not impact the error handling.

Signed-off-by: Goffredo Baroncelli 
Reviewed-by: Daniel Kiper 
---
 grub-core/fs/btrfs.c | 24 +++-
 1 file changed, 23 insertions(+), 1 deletion(-)

diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
index 933a57d3b..c9f0c4193 100644
--- a/grub-core/fs/btrfs.c
+++ b/grub-core/fs/btrfs.c
@@ -77,7 +77,8 @@ struct btrfs_header
 {
   grub_btrfs_checksum_t checksum;
   grub_btrfs_uuid_t uuid;
-  grub_uint8_t dummy[0x30];
+  grub_uint64_t bytenr;
+  grub_uint8_t dummy[0x28];
   grub_uint32_t nitems;
   grub_uint8_t level;
 } GRUB_PACKED;
@@ -286,6 +287,25 @@ free_iterator (struct grub_btrfs_leaf_descriptor *desc)
   grub_free (desc->data);
 }
 
+static grub_err_t
+check_btrfs_header (struct grub_btrfs_data *data, struct btrfs_header *header,
+grub_disk_addr_t addr)
+{
+  if (grub_le_to_cpu64 (header->bytenr) != addr)
+{
+  grub_dprintf ("btrfs", "btrfs_header.bytenr is not equal node addr\n");
+  return grub_error (GRUB_ERR_BAD_FS,
+"header bytenr is not equal node addr");
+}
+  if (grub_memcmp (data->sblock.uuid, header->uuid, sizeof(grub_btrfs_uuid_t)))
+{
+  grub_dprintf ("btrfs", "btrfs_header.uuid doesn't match sblock uuid\n");
+  return grub_error (GRUB_ERR_BAD_FS,
+"header uuid doesn't match sblock uuid");
+}
+  return GRUB_ERR_NONE;
+}
+
 static grub_err_t
 save_ref (struct grub_btrfs_leaf_descriptor *desc,
  grub_disk_addr_t addr, unsigned i, unsigned m, int l)
@@ -341,6 +361,7 @@ next (struct grub_btrfs_data *data,
 
   err = grub_btrfs_read_logical (data, grub_le_to_cpu64 (node.addr),
 , sizeof (head), 0);
+  check_btrfs_header (data, , grub_le_to_cpu64 (node.addr));
   if (err)
return -err;
 
@@ -402,6 +423,7 @@ lower_bound (struct grub_btrfs_data *data,
   /* FIXME: preread few nodes into buffer. */
   err = grub_btrfs_read_logical (data, addr, , sizeof (head),
 recursion_depth + 1);
+  check_btrfs_header (data, , addr);
   if (err)
return err;
   addr += sizeof (head);
-- 
2.19.1


___
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel


Re: [PATCH 7/9] btrfs: Add support for recovery for a RAID 5 btrfs profiles.

2018-10-10 Thread Goffredo Baroncelli
On 09/10/2018 20.20, Daniel Kiper wrote:
> On Thu, Sep 27, 2018 at 08:35:02PM +0200, Goffredo Baroncelli wrote:
>> From: Goffredo Baroncelli 
>>
>> Add support for recovery for a RAID 5 btrfs profile. In addition
>> it is added some code as preparatory work for RAID 6 recovery code.
>>
>> Signed-off-by: Goffredo Baroncelli 
>> ---
>>  grub-core/fs/btrfs.c | 160 +--
>>  1 file changed, 155 insertions(+), 5 deletions(-)
>>
>> diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
>> index 554f350c5..db8df0eea 100644
>> --- a/grub-core/fs/btrfs.c
>> +++ b/grub-core/fs/btrfs.c
>> @@ -29,6 +29,7 @@
[...]

>> +
>> +  stripe = (struct grub_btrfs_chunk_stripe *) (chunk + 1) + i;
> 
> I think that "chunk + 1" requires short comment why...

This is a quite common pattern (a struct followed by an array of struct). 
However I added a short comment like

  /* after the struct grub_btrfs_chunk_item, there is an array of 
 struct grub_btrfs_chunk_stripe */
  stripe = (struct grub_btrfs_chunk_stripe *) (chunk + 1) + i;


[...]
>> -for (i = 0; i < redundancy; i++)
>> +if (!is_raid56)
> 
> Why not "if (is_raid56)"? This looks more natural here.

I think that it was due to the fact than before it was only the not "raid5/6" 
profiles.
Then it was added the "raid5/6" profiles, so I added this case after.

What I dislike is not the order, but the fact that in one case (not raid5), the 
same function is called several time until it found valid data. In the other 
case, it is called only the first time, then a completely different path is 
called
I am trying a different solution, but in any case the result is a bit ugly


> 
>> +  for (i = 0; i < redundancy; i++)
>> +{
>> +  err = btrfs_read_from_chunk (data, chunk, stripen,
>> +   stripe_offset,
>> +   i, /* redundancy */
>> +   csize, buf);
>> +  if (!err)
>> +break;
>> +  grub_errno = GRUB_ERR_NONE;
>> +}
>> +else
>>{
>>  err = btrfs_read_from_chunk (data, chunk, stripen,
>>   stripe_offset,
>> - i, /* redundancy */
>> + 0, /* no mirror */
>>   csize, buf);
>> -if (!err)
>> -  break;
>>  grub_errno = GRUB_ERR_NONE;
>> +if (err != GRUB_ERR_NONE)
> 
> Please be consistent and use "if (err)" here.
ok
> 
>> +  err = raid56_read_retry (data, chunk, stripe_offset,
>> +   csize, buf);
>>}
>> -if (i != redundancy)
>> +if (err == GRUB_ERR_NONE)
> 
> if (!err) please...
ok
> 
> Daniel
> 
> ___
> Grub-devel mailing list
> Grub-devel@gnu.org
> https://lists.gnu.org/mailman/listinfo/grub-devel
> 


-- 
gpg @keyserver.linux.it: Goffredo Baroncelli 
Key fingerprint BBF5 1610 0B64 DAC6 5F7D  17B2 0EDA 9B37 8B82 E0B5

___
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel


[PATCH 9/9] btrfs: Add RAID 6 recovery for a btrfs filesystem.

2018-09-27 Thread Goffredo Baroncelli
From: Goffredo Baroncelli 

Add the RAID 6 recovery, in order to use a RAID 6 filesystem even if some
disks (up to two) are missing. This code use the md RAID 6 code already
present in grub.

Signed-off-by: Goffredo Baroncelli 
---
 grub-core/fs/btrfs.c | 63 +++-
 1 file changed, 57 insertions(+), 6 deletions(-)

diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
index db8df0eea..b49f714ef 100644
--- a/grub-core/fs/btrfs.c
+++ b/grub-core/fs/btrfs.c
@@ -30,6 +30,7 @@
 #include 
 #include 
 #include 
+#include 
 
 GRUB_MOD_LICENSE ("GPLv3+");
 
@@ -702,11 +703,36 @@ rebuild_raid5 (char *dest, struct raid56_buffer *buffers,
 }
 }
 
+static grub_err_t
+raid6_recover_read_buffer (void *data, int disk_nr,
+  grub_uint64_t addr __attribute__ ((unused)),
+  void *dest, grub_size_t size)
+{
+struct raid56_buffer *buffers = data;
+
+if (!buffers[disk_nr].data_is_valid)
+   return grub_errno = GRUB_ERR_READ_ERROR;
+
+grub_memcpy(dest, buffers[disk_nr].buf, size);
+
+return grub_errno = GRUB_ERR_NONE;
+}
+
+static void
+rebuild_raid6 (struct raid56_buffer *buffers, grub_uint64_t nstripes,
+   grub_uint64_t csize, grub_uint64_t parities_pos, void *dest,
+   grub_uint64_t stripen)
+
+{
+  grub_raid6_recover_gen (buffers, nstripes, stripen, parities_pos,
+  dest, 0, csize, 0, raid6_recover_read_buffer);
+}
+
 static grub_err_t
 raid56_read_retry (struct grub_btrfs_data *data,
   struct grub_btrfs_chunk_item *chunk,
-  grub_uint64_t stripe_offset,
-  grub_uint64_t csize, void *buf)
+  grub_uint64_t stripe_offset, grub_uint64_t stripen,
+  grub_uint64_t csize, void *buf, grub_uint64_t parities_pos)
 {
   struct raid56_buffer *buffers;
   grub_uint64_t nstripes = grub_le_to_cpu16 (chunk->nstripes);
@@ -777,6 +803,15 @@ raid56_read_retry (struct grub_btrfs_data *data,
   ret = GRUB_ERR_READ_ERROR;
   goto cleanup;
 }
+  else if (failed_devices > 2 && (chunk_type & GRUB_BTRFS_CHUNK_TYPE_RAID6))
+{
+  grub_dprintf ("btrfs",
+   "not enough disks for raid6: total %" PRIuGRUB_UINT64_T
+   ", missing %" PRIuGRUB_UINT64_T "\n",
+   nstripes, failed_devices);
+  ret = GRUB_ERR_READ_ERROR;
+  goto cleanup;
+}
   else
 grub_dprintf ("btrfs",
  "enough disks for RAID 5 rebuilding: total %"
@@ -787,7 +822,7 @@ raid56_read_retry (struct grub_btrfs_data *data,
   if (chunk_type & GRUB_BTRFS_CHUNK_TYPE_RAID5)
 rebuild_raid5 (buf, buffers, nstripes, csize);
   else
-grub_dprintf ("btrfs", "called rebuild_raid6(), NOT IMPLEMENTED\n");
+rebuild_raid6 (buffers, nstripes, csize, parities_pos, buf, stripen);
 
   ret = GRUB_ERR_NONE;
  cleanup:
@@ -877,9 +912,11 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, 
grub_disk_addr_t addr,
unsigned redundancy = 1;
unsigned i, j;
int is_raid56;
+   grub_uint64_t parities_pos = 0;
 
-   is_raid56 = !!(grub_le_to_cpu64 (chunk->type) &
-  GRUB_BTRFS_CHUNK_TYPE_RAID5);
+is_raid56 = !!(grub_le_to_cpu64 (chunk->type) &
+  (GRUB_BTRFS_CHUNK_TYPE_RAID5 |
+   GRUB_BTRFS_CHUNK_TYPE_RAID6));
 
if (grub_le_to_cpu64 (chunk->size) <= off)
  {
@@ -1011,6 +1048,9 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, 
grub_disk_addr_t addr,
   *size if the latter is smaller.
   *  - nparities is the number of parities (1 for RAID5, 2 for
   *RAID6); used only in RAID5/6 code.
+  *size if the latter is smaller.
+  *  - parities_pos is the position of the parity in a row (
+  *2 for P1, 3 for P2...)
   */
  stripe_nr = grub_divmod64 (off, chunk_stripe_length, );
 
@@ -1029,6 +1069,17 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, 
grub_disk_addr_t addr,
   */
  grub_divmod64 (high + stripen, nstripes, );
 
+ /*
+  * parities_pos is equal to "(high - nparities) % nstripes"
+  * (see the diagram above).
+  * However "high - nparities" might be negative (eg when high
+  * == 0) leading to an incorrect computation.
+  * Instead "high + nstripes - nparities" is always positive and
+  * in modulo nstripes is equal to "(high - nparities) % nstripes"
+  */
+ grub_divmod64 (high + nstripes - nparities, nstripes,
+_pos);
+
  stripe_offset = low + chunk_stripe_length * high;
  

[PATCH 3/9] btrfs: Move the error logging from find_device() to its caller.

2018-09-27 Thread Goffredo Baroncelli
From: Goffredo Baroncelli 

The caller knows better if this error is fatal or not, i.e. another disk is
available or not.

This is a preparatory patch.

Signed-off-by: Goffredo Baroncelli 
Reviewed-by: Daniel Kiper 
---
 grub-core/fs/btrfs.c | 10 --
 1 file changed, 4 insertions(+), 6 deletions(-)

diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
index bf0dbce21..2b1dd7dd4 100644
--- a/grub-core/fs/btrfs.c
+++ b/grub-core/fs/btrfs.c
@@ -603,12 +603,7 @@ find_device (struct grub_btrfs_data *data, grub_uint64_t 
id, int do_rescan)
   if (do_rescan)
 grub_device_iterate (find_device_iter, );
   if (!ctx.dev_found)
-{
-  grub_error (GRUB_ERR_BAD_FS,
- N_("couldn't find a necessary member device "
-"of multi-device filesystem"));
-  return NULL;
-}
+return NULL;
   data->n_devices_attached++;
   if (data->n_devices_attached > data->n_devices_allocated)
 {
@@ -906,6 +901,9 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, 
grub_disk_addr_t addr,
dev = find_device (data, stripe->device_id, j);
if (!dev)
  {
+   grub_dprintf ("btrfs",
+ "couldn't find a necessary member device "
+ "of multi-device filesystem\n");
err = grub_errno;
grub_errno = GRUB_ERR_NONE;
continue;
-- 
2.19.0


___
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel


[PATCH 2/9] btrfs: Add helper to check the btrfs header.

2018-09-27 Thread Goffredo Baroncelli
From: Goffredo Baroncelli 

This helper is used in a few places to help the debugging. As
conservative approach the error is only logged.
This does not impact the error handling.

Signed-off-by: Goffredo Baroncelli 
Reviewed-by: Daniel Kiper 
---
 grub-core/fs/btrfs.c | 24 +++-
 1 file changed, 23 insertions(+), 1 deletion(-)

diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
index 9bc6d399d..bf0dbce21 100644
--- a/grub-core/fs/btrfs.c
+++ b/grub-core/fs/btrfs.c
@@ -77,7 +77,8 @@ struct btrfs_header
 {
   grub_btrfs_checksum_t checksum;
   grub_btrfs_uuid_t uuid;
-  grub_uint8_t dummy[0x30];
+  grub_uint64_t bytenr;
+  grub_uint8_t dummy[0x28];
   grub_uint32_t nitems;
   grub_uint8_t level;
 } GRUB_PACKED;
@@ -286,6 +287,25 @@ free_iterator (struct grub_btrfs_leaf_descriptor *desc)
   grub_free (desc->data);
 }
 
+static grub_err_t
+check_btrfs_header (struct grub_btrfs_data *data, struct btrfs_header *header,
+grub_disk_addr_t addr)
+{
+  if (grub_le_to_cpu64 (header->bytenr) != addr)
+{
+  grub_dprintf ("btrfs", "btrfs_header.bytenr is not equal node addr\n");
+  return grub_error (GRUB_ERR_BAD_FS,
+"header bytenr is not equal node addr");
+}
+  if (grub_memcmp (data->sblock.uuid, header->uuid, sizeof(grub_btrfs_uuid_t)))
+{
+  grub_dprintf ("btrfs", "btrfs_header.uuid doesn't match sblock uuid\n");
+  return grub_error (GRUB_ERR_BAD_FS,
+"header uuid doesn't match sblock uuid");
+}
+  return GRUB_ERR_NONE;
+}
+
 static grub_err_t
 save_ref (struct grub_btrfs_leaf_descriptor *desc,
  grub_disk_addr_t addr, unsigned i, unsigned m, int l)
@@ -341,6 +361,7 @@ next (struct grub_btrfs_data *data,
 
   err = grub_btrfs_read_logical (data, grub_le_to_cpu64 (node.addr),
 , sizeof (head), 0);
+  check_btrfs_header (data, , grub_le_to_cpu64 (node.addr));
   if (err)
return -err;
 
@@ -402,6 +423,7 @@ lower_bound (struct grub_btrfs_data *data,
   /* FIXME: preread few nodes into buffer. */
   err = grub_btrfs_read_logical (data, addr, , sizeof (head),
 recursion_depth + 1);
+  check_btrfs_header (data, , addr);
   if (err)
return err;
   addr += sizeof (head);
-- 
2.19.0


___
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel


[PATCH 8/9] btrfs: Make more generic the code for RAID 6 rebuilding

2018-09-27 Thread Goffredo Baroncelli
From: Goffredo Baroncelli 

The original code which handles the recovery of a RAID 6 disks array
assumes that all reads are multiple of 1 << GRUB_DISK_SECTOR_BITS and it
assumes that all the I/O is done via the struct grub_diskfilter_segment.
This is not true for the btrfs code. In order to reuse the native
grub_raid6_recover() code, it is modified to not call
grub_diskfilter_read_node() directly, but to call an handler passed
as an argument.

Signed-off-by: Goffredo Baroncelli 
Reviewed-by: Daniel Kiper 
---
 grub-core/disk/raid6_recover.c | 52 ++
 include/grub/diskfilter.h  |  9 ++
 2 files changed, 43 insertions(+), 18 deletions(-)

diff --git a/grub-core/disk/raid6_recover.c b/grub-core/disk/raid6_recover.c
index aa674f6ca..0cf691ddf 100644
--- a/grub-core/disk/raid6_recover.c
+++ b/grub-core/disk/raid6_recover.c
@@ -74,14 +74,26 @@ mod_255 (unsigned x)
 }
 
 static grub_err_t
-grub_raid6_recover (struct grub_diskfilter_segment *array, int disknr, int p,
-char *buf, grub_disk_addr_t sector, grub_size_t size)
+raid6_recover_read_node (void *data, int disknr,
+   grub_uint64_t sector,
+   void *buf, grub_size_t size)
+{
+struct grub_diskfilter_segment *array = data;
+
+return grub_diskfilter_read_node (>nodes[disknr],
+ (grub_disk_addr_t)sector,
+ size >> GRUB_DISK_SECTOR_BITS, buf);
+}
+
+grub_err_t
+grub_raid6_recover_gen (void *data, grub_uint64_t nstripes, int disknr, int p,
+   char *buf, grub_uint64_t sector, grub_size_t size,
+   int layout, raid_recover_read_t read_func)
 {
   int i, q, pos;
   int bad1 = -1, bad2 = -1;
   char *pbuf = 0, *qbuf = 0;
 
-  size <<= GRUB_DISK_SECTOR_BITS;
   pbuf = grub_zalloc (size);
   if (!pbuf)
 goto quit;
@@ -91,17 +103,17 @@ grub_raid6_recover (struct grub_diskfilter_segment *array, 
int disknr, int p,
 goto quit;
 
   q = p + 1;
-  if (q == (int) array->node_count)
+  if (q == (int) nstripes)
 q = 0;
 
   pos = q + 1;
-  if (pos == (int) array->node_count)
+  if (pos == (int) nstripes)
 pos = 0;
 
-  for (i = 0; i < (int) array->node_count - 2; i++)
+  for (i = 0; i < (int) nstripes - 2; i++)
 {
   int c;
-  if (array->layout & GRUB_RAID_LAYOUT_MUL_FROM_POS)
+  if (layout & GRUB_RAID_LAYOUT_MUL_FROM_POS)
c = pos;
   else
c = i;
@@ -109,8 +121,7 @@ grub_raid6_recover (struct grub_diskfilter_segment *array, 
int disknr, int p,
 bad1 = c;
   else
 {
-  if (! grub_diskfilter_read_node (>nodes[pos], sector,
-  size >> GRUB_DISK_SECTOR_BITS, buf))
+ if (!read_func(data, pos, sector, buf, size))
 {
   grub_crypto_xor (pbuf, pbuf, buf, size);
   grub_raid_block_mulx (c, buf, size);
@@ -128,7 +139,7 @@ grub_raid6_recover (struct grub_diskfilter_segment *array, 
int disknr, int p,
 }
 
   pos++;
-  if (pos == (int) array->node_count)
+  if (pos == (int) nstripes)
 pos = 0;
 }
 
@@ -139,16 +150,14 @@ grub_raid6_recover (struct grub_diskfilter_segment 
*array, int disknr, int p,
   if (bad2 < 0)
 {
   /* One bad device */
-  if ((! grub_diskfilter_read_node (>nodes[p], sector,
-   size >> GRUB_DISK_SECTOR_BITS, buf)))
+  if (!read_func(data, p, sector, buf, size))
 {
   grub_crypto_xor (buf, buf, pbuf, size);
   goto quit;
 }
 
   grub_errno = GRUB_ERR_NONE;
-  if (grub_diskfilter_read_node (>nodes[q], sector,
-size >> GRUB_DISK_SECTOR_BITS, buf))
+  if (read_func(data, q, sector, buf, size))
 goto quit;
 
   grub_crypto_xor (buf, buf, qbuf, size);
@@ -160,14 +169,12 @@ grub_raid6_recover (struct grub_diskfilter_segment 
*array, int disknr, int p,
   /* Two bad devices */
   unsigned c;
 
-  if (grub_diskfilter_read_node (>nodes[p], sector,
-size >> GRUB_DISK_SECTOR_BITS, buf))
+  if (read_func(data, p, sector, buf, size))
 goto quit;
 
   grub_crypto_xor (pbuf, pbuf, buf, size);
 
-  if (grub_diskfilter_read_node (>nodes[q], sector,
-size >> GRUB_DISK_SECTOR_BITS, buf))
+  if (read_func(data, q, sector, buf, size))
 goto quit;
 
   grub_crypto_xor (qbuf, qbuf, buf, size);
@@ -190,6 +197,15 @@ quit:
   return grub_errno;
 }
 
+static grub_err_t
+grub_raid6_recover (struct grub_diskfilter_segment *array, int disknr, int p,
+char *buf, grub_disk_addr_t sector, grub_size_t size)
+{
+  return grub_raid6_recover_gen (array, array->n

[PATCH 7/9] btrfs: Add support for recovery for a RAID 5 btrfs profiles.

2018-09-27 Thread Goffredo Baroncelli
From: Goffredo Baroncelli 

Add support for recovery for a RAID 5 btrfs profile. In addition
it is added some code as preparatory work for RAID 6 recovery code.

Signed-off-by: Goffredo Baroncelli 
---
 grub-core/fs/btrfs.c | 160 +--
 1 file changed, 155 insertions(+), 5 deletions(-)

diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
index 554f350c5..db8df0eea 100644
--- a/grub-core/fs/btrfs.c
+++ b/grub-core/fs/btrfs.c
@@ -29,6 +29,7 @@
 #include 
 #include 
 #include 
+#include 
 
 GRUB_MOD_LICENSE ("GPLv3+");
 
@@ -665,6 +666,139 @@ btrfs_read_from_chunk (struct grub_btrfs_data *data,
 return err;
 }
 
+struct raid56_buffer {
+  void *buf;
+  int  data_is_valid;
+};
+
+static void
+rebuild_raid5 (char *dest, struct raid56_buffer *buffers,
+  grub_uint64_t nstripes, grub_uint64_t csize)
+{
+  grub_uint64_t i;
+  int first;
+
+  for(i = 0; buffers[i].data_is_valid && i < nstripes; i++);
+
+  if (i == nstripes)
+{
+  grub_dprintf ("btrfs", "called rebuild_raid5(), but all disks are OK\n");
+  return;
+}
+
+  grub_dprintf ("btrfs", "rebuilding RAID 5 stripe #%" PRIuGRUB_UINT64_T "\n", 
i);
+
+  for (i = 0, first = 1; i < nstripes; i++)
+{
+  if (!buffers[i].data_is_valid)
+   continue;
+
+  if (first) {
+   grub_memcpy(dest, buffers[i].buf, csize);
+   first = 0;
+  } else
+   grub_crypto_xor (dest, dest, buffers[i].buf, csize);
+
+}
+}
+
+static grub_err_t
+raid56_read_retry (struct grub_btrfs_data *data,
+  struct grub_btrfs_chunk_item *chunk,
+  grub_uint64_t stripe_offset,
+  grub_uint64_t csize, void *buf)
+{
+  struct raid56_buffer *buffers;
+  grub_uint64_t nstripes = grub_le_to_cpu16 (chunk->nstripes);
+  grub_uint64_t chunk_type = grub_le_to_cpu64 (chunk->type);
+  grub_err_t ret = GRUB_ERR_OUT_OF_MEMORY;
+  grub_uint64_t i, failed_devices;
+
+  buffers = grub_zalloc (sizeof(*buffers) * nstripes);
+  if (!buffers)
+goto cleanup;
+
+  for (i = 0; i < nstripes; i++)
+{
+  buffers[i].buf = grub_zalloc (csize);
+  if (!buffers[i].buf)
+   goto cleanup;
+}
+
+  for (failed_devices = 0, i = 0; i < nstripes; i++)
+{
+  struct grub_btrfs_chunk_stripe *stripe;
+  grub_disk_addr_t paddr;
+  grub_device_t dev;
+  grub_err_t err;
+
+  stripe = (struct grub_btrfs_chunk_stripe *) (chunk + 1) + i;
+
+  paddr = grub_le_to_cpu64 (stripe->offset) + stripe_offset;
+  grub_dprintf ("btrfs", "reading paddr %" PRIxGRUB_UINT64_T
+" from stripe ID %" PRIxGRUB_UINT64_T "\n", paddr,
+stripe->device_id);
+
+  dev = find_device (data, stripe->device_id);
+  if (!dev)
+   {
+ buffers[i].data_is_valid = 0;
+ grub_dprintf ("btrfs", "stripe %" PRIuGRUB_UINT64_T " FAILED (dev ID 
%"
+   PRIxGRUB_UINT64_T ")\n", i, stripe->device_id);
+ failed_devices++;
+ continue;
+   }
+
+  err = grub_disk_read (dev->disk, paddr >> GRUB_DISK_SECTOR_BITS,
+   paddr & (GRUB_DISK_SECTOR_SIZE - 1),
+   csize, buffers[i].buf);
+  if (err == GRUB_ERR_NONE)
+   {
+ buffers[i].data_is_valid = 1;
+ grub_dprintf ("btrfs", "stripe %" PRIuGRUB_UINT64_T " Ok (dev ID %"
+   PRIxGRUB_UINT64_T ")\n", i, stripe->device_id);
+   }
+  else
+   {
+ buffers[i].data_is_valid = 0;
+ grub_dprintf ("btrfs", "stripe %" PRIuGRUB_UINT64_T
+   " FAILED (dev ID %" PRIxGRUB_UINT64_T ")\n", i,
+   stripe->device_id);
+ failed_devices++;
+   }
+}
+
+  if (failed_devices > 1 && (chunk_type & GRUB_BTRFS_CHUNK_TYPE_RAID5))
+{
+  grub_dprintf ("btrfs",
+   "not enough disks for RAID 5: total %" PRIuGRUB_UINT64_T
+   ", missing %" PRIuGRUB_UINT64_T "\n",
+   nstripes, failed_devices);
+  ret = GRUB_ERR_READ_ERROR;
+  goto cleanup;
+}
+  else
+grub_dprintf ("btrfs",
+ "enough disks for RAID 5 rebuilding: total %"
+ PRIuGRUB_UINT64_T ", missing %" PRIuGRUB_UINT64_T "\n",
+ nstripes, failed_devices);
+
+  /* if these are enough, try to rebuild the data */
+  if (chunk_type & GRUB_BTRFS_CHUNK_TYPE_RAID5)
+rebuild_raid5 (buf, buffers, nstripes, csize);
+  else
+grub_dprintf ("btrfs", "called rebuild_raid6(), NOT IMPLEMENTED\n");
+
+  ret = GRUB_ERR_NONE;
+ cleanup:
+  if (

[PATCH 5/9] btrfs: Move logging code in grub_btrfs_read_logical()

2018-09-27 Thread Goffredo Baroncelli
From: Goffredo Baroncelli 

A portion of the logging code is moved outside of internal for(;;). The part
that is left inside is the one which depends on the internal for(;;) index.

This is a preparatory patch. The next one will refactor the code inside
the for(;;) into an another function.

Signed-off-by: Goffredo Baroncelli 
Reviewed-by: Daniel Kiper 
---
 grub-core/fs/btrfs.c | 25 ++---
 1 file changed, 14 insertions(+), 11 deletions(-)

diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
index c07d91657..179a2da9d 100644
--- a/grub-core/fs/btrfs.c
+++ b/grub-core/fs/btrfs.c
@@ -871,6 +871,18 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, 
grub_disk_addr_t addr,
 
for (j = 0; j < 2; j++)
  {
+   grub_dprintf ("btrfs", "chunk 0x%" PRIxGRUB_UINT64_T
+ "+0x%" PRIxGRUB_UINT64_T
+ " (%d stripes (%d substripes) of %"
+ PRIxGRUB_UINT64_T ")\n",
+ grub_le_to_cpu64 (key->offset),
+ grub_le_to_cpu64 (chunk->size),
+ grub_le_to_cpu16 (chunk->nstripes),
+ grub_le_to_cpu16 (chunk->nsubstripes),
+ grub_le_to_cpu64 (chunk->stripe_length));
+   grub_dprintf ("btrfs", "reading laddr 0x%" PRIxGRUB_UINT64_T "\n",
+ addr);
+
for (i = 0; i < redundancy; i++)
  {
struct grub_btrfs_chunk_stripe *stripe;
@@ -883,20 +895,11 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, 
grub_disk_addr_t addr,
 
paddr = grub_le_to_cpu64 (stripe->offset) + stripe_offset;
 
-   grub_dprintf ("btrfs", "chunk 0x%" PRIxGRUB_UINT64_T
- "+0x%" PRIxGRUB_UINT64_T
- " (%d stripes (%d substripes) of %"
- PRIxGRUB_UINT64_T ") stripe %" PRIxGRUB_UINT64_T
+   grub_dprintf ("btrfs", "stripe %" PRIxGRUB_UINT64_T
  " maps to 0x%" PRIxGRUB_UINT64_T "\n",
- grub_le_to_cpu64 (key->offset),
- grub_le_to_cpu64 (chunk->size),
- grub_le_to_cpu16 (chunk->nstripes),
- grub_le_to_cpu16 (chunk->nsubstripes),
- grub_le_to_cpu64 (chunk->stripe_length),
  stripen, stripe->offset);
grub_dprintf ("btrfs", "reading paddr 0x%" PRIxGRUB_UINT64_T
- " for laddr 0x%" PRIxGRUB_UINT64_T "\n", paddr,
- addr);
+ "\n", paddr);
 
dev = find_device (data, stripe->device_id);
if (!dev)
-- 
2.19.0


___
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel


[PATCH 4/9] btrfs: Avoid a rescan for a device which was already not found.

2018-09-27 Thread Goffredo Baroncelli
From: Goffredo Baroncelli 

Change the behavior of find_device(): before the patch, a read of a missed
device may trigger a rescan. However, it is never recorded that a device
is missed, so each single read of a missed device may triggers a rescan.
It is the caller who decides if a rescan is performed in case of a missed
device. And it does quite often, without considering if in the past a
devices was already found as "missed"
This behavior causes a lot of unneeded rescan, causing a huge slowdown in
case of a missed device.

After the patch, the "missed device" information is stored in the
data->devices_attached[] array (as a NULL value in the field dev). A rescan
is triggered only if no information at all is found. This means that only
the first time a read of a missed device triggers a rescan.

The change in the code is done removing "return NULL" when the disk is not
found. So it is always executed the code which stores in the
data->devices_attached[] array the value returned by grub_device_iterate():
NULL if the device is missed, or a valid data otherwise.

Signed-off-by: Goffredo Baroncelli 
---
 grub-core/fs/btrfs.c | 17 +
 1 file changed, 9 insertions(+), 8 deletions(-)

diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
index 2b1dd7dd4..c07d91657 100644
--- a/grub-core/fs/btrfs.c
+++ b/grub-core/fs/btrfs.c
@@ -588,7 +588,7 @@ find_device_iter (const char *name, void *data)
 }
 
 static grub_device_t
-find_device (struct grub_btrfs_data *data, grub_uint64_t id, int do_rescan)
+find_device (struct grub_btrfs_data *data, grub_uint64_t id)
 {
   struct find_device_ctx ctx = {
 .data = data,
@@ -600,10 +600,9 @@ find_device (struct grub_btrfs_data *data, grub_uint64_t 
id, int do_rescan)
   for (i = 0; i < data->n_devices_attached; i++)
 if (id == data->devices_attached[i].id)
   return data->devices_attached[i].dev;
-  if (do_rescan)
-grub_device_iterate (find_device_iter, );
-  if (!ctx.dev_found)
-return NULL;
+
+  grub_device_iterate (find_device_iter, );
+
   data->n_devices_attached++;
   if (data->n_devices_attached > data->n_devices_allocated)
 {
@@ -615,7 +614,8 @@ find_device (struct grub_btrfs_data *data, grub_uint64_t 
id, int do_rescan)
* sizeof (data->devices_attached[0]));
   if (!data->devices_attached)
{
- grub_device_close (ctx.dev_found);
+ if (ctx.dev_found)
+   grub_device_close (ctx.dev_found);
  data->devices_attached = tmp;
  return NULL;
}
@@ -898,7 +898,7 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, 
grub_disk_addr_t addr,
  " for laddr 0x%" PRIxGRUB_UINT64_T "\n", paddr,
  addr);
 
-   dev = find_device (data, stripe->device_id, j);
+   dev = find_device (data, stripe->device_id);
if (!dev)
  {
grub_dprintf ("btrfs",
@@ -975,7 +975,8 @@ grub_btrfs_unmount (struct grub_btrfs_data *data)
   unsigned i;
   /* The device 0 is closed one layer upper.  */
   for (i = 1; i < data->n_devices_attached; i++)
-grub_device_close (data->devices_attached[i].dev);
+if (data->devices_attached[i].dev)
+grub_device_close (data->devices_attached[i].dev);
   grub_free (data->devices_attached);
   grub_free (data->extent);
   grub_free (data);
-- 
2.19.0


___
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel


[PATCH 6/9] btrfs: Refactor the code that read from disk

2018-09-27 Thread Goffredo Baroncelli
From: Goffredo Baroncelli 

Move the code in charge to read the data from disk into a separate
function. This helps to separate the error handling logic (which depends on
the different raid profiles) from the read from disk logic.
Refactoring this code increases the general readability too.

This is a preparatory patch, to help the adding of the RAID 5/6 recovery
code.

Signed-off-by: Goffredo Baroncelli 
Reviewed-by: Daniel Kiper 
---
 grub-core/fs/btrfs.c | 75 ++--
 1 file changed, 44 insertions(+), 31 deletions(-)

diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
index 179a2da9d..554f350c5 100644
--- a/grub-core/fs/btrfs.c
+++ b/grub-core/fs/btrfs.c
@@ -625,6 +625,46 @@ find_device (struct grub_btrfs_data *data, grub_uint64_t 
id)
   return ctx.dev_found;
 }
 
+static grub_err_t
+btrfs_read_from_chunk (struct grub_btrfs_data *data,
+  struct grub_btrfs_chunk_item *chunk,
+  grub_uint64_t stripen, grub_uint64_t stripe_offset,
+  int redundancy, grub_uint64_t csize,
+  void *buf)
+{
+struct grub_btrfs_chunk_stripe *stripe;
+grub_disk_addr_t paddr;
+grub_device_t dev;
+grub_err_t err;
+
+stripe = (struct grub_btrfs_chunk_stripe *) (chunk + 1);
+/* Right now the redundancy handling is easy.
+   With RAID5-like it will be more difficult.  */
+stripe += stripen + redundancy;
+
+paddr = grub_le_to_cpu64 (stripe->offset) + stripe_offset;
+
+grub_dprintf ("btrfs", "stripe %" PRIxGRUB_UINT64_T
+ " maps to 0x%" PRIxGRUB_UINT64_T
+ ". Reading paddr 0x%" PRIxGRUB_UINT64_T "\n",
+ stripen, stripe->offset, paddr);
+
+dev = find_device (data, stripe->device_id);
+if (!dev)
+  {
+   grub_dprintf ("btrfs",
+ "couldn't find a necessary member device "
+ "of multi-device filesystem\n");
+   grub_errno = GRUB_ERR_NONE;
+   return GRUB_ERR_READ_ERROR;
+  }
+
+err = grub_disk_read (dev->disk, paddr >> GRUB_DISK_SECTOR_BITS,
+ paddr & (GRUB_DISK_SECTOR_SIZE - 1),
+ csize, buf);
+return err;
+}
+
 static grub_err_t
 grub_btrfs_read_logical (struct grub_btrfs_data *data, grub_disk_addr_t addr,
 void *buf, grub_size_t size, int recursion_depth)
@@ -638,7 +678,6 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, 
grub_disk_addr_t addr,
   grub_err_t err = 0;
   struct grub_btrfs_key key_out;
   int challoc = 0;
-  grub_device_t dev;
   struct grub_btrfs_key key_in;
   grub_size_t chsize;
   grub_disk_addr_t chaddr;
@@ -885,36 +924,10 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, 
grub_disk_addr_t addr,
 
for (i = 0; i < redundancy; i++)
  {
-   struct grub_btrfs_chunk_stripe *stripe;
-   grub_disk_addr_t paddr;
-
-   stripe = (struct grub_btrfs_chunk_stripe *) (chunk + 1);
-   /* Right now the redundancy handling is easy.
-  With RAID5-like it will be more difficult.  */
-   stripe += stripen + i;
-
-   paddr = grub_le_to_cpu64 (stripe->offset) + stripe_offset;
-
-   grub_dprintf ("btrfs", "stripe %" PRIxGRUB_UINT64_T
- " maps to 0x%" PRIxGRUB_UINT64_T "\n",
- stripen, stripe->offset);
-   grub_dprintf ("btrfs", "reading paddr 0x%" PRIxGRUB_UINT64_T
- "\n", paddr);
-
-   dev = find_device (data, stripe->device_id);
-   if (!dev)
- {
-   grub_dprintf ("btrfs",
- "couldn't find a necessary member device "
- "of multi-device filesystem\n");
-   err = grub_errno;
-   grub_errno = GRUB_ERR_NONE;
-   continue;
- }
-
-   err = grub_disk_read (dev->disk, paddr >> GRUB_DISK_SECTOR_BITS,
- paddr & (GRUB_DISK_SECTOR_SIZE - 1),
- csize, buf);
+   err = btrfs_read_from_chunk (data, chunk, stripen,
+stripe_offset,
+i, /* redundancy */
+csize, buf);
if (!err)
  break;
grub_errno = GRUB_ERR_NONE;
-- 
2.19.0


___
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel


[PATCH 1/9] btrfs: Add support for reading a filesystem with a RAID 5 or RAID 6 profile.

2018-09-27 Thread Goffredo Baroncelli
From: Goffredo Baroncelli 

Signed-off-by: Goffredo Baroncelli 
---
 grub-core/fs/btrfs.c | 74 
 1 file changed, 74 insertions(+)

diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
index be195448d..9bc6d399d 100644
--- a/grub-core/fs/btrfs.c
+++ b/grub-core/fs/btrfs.c
@@ -119,6 +119,8 @@ struct grub_btrfs_chunk_item
 #define GRUB_BTRFS_CHUNK_TYPE_RAID1 0x10
 #define GRUB_BTRFS_CHUNK_TYPE_DUPLICATED0x20
 #define GRUB_BTRFS_CHUNK_TYPE_RAID100x40
+#define GRUB_BTRFS_CHUNK_TYPE_RAID5 0x80
+#define GRUB_BTRFS_CHUNK_TYPE_RAID6 0x100
   grub_uint8_t dummy2[0xc];
   grub_uint16_t nstripes;
   grub_uint16_t nsubstripes;
@@ -764,6 +766,78 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, 
grub_disk_addr_t addr,
  stripe_offset = low + chunk_stripe_length
* high;
  csize = chunk_stripe_length - low;
+ break;
+   }
+ case GRUB_BTRFS_CHUNK_TYPE_RAID5:
+ case GRUB_BTRFS_CHUNK_TYPE_RAID6:
+   {
+ grub_uint64_t nparities, stripe_nr, high, low;
+
+ redundancy = 1;   /* no redundancy for now */
+
+ if (grub_le_to_cpu64 (chunk->type) & GRUB_BTRFS_CHUNK_TYPE_RAID5)
+   {
+ grub_dprintf ("btrfs", "RAID5\n");
+ nparities = 1;
+   }
+ else
+   {
+ grub_dprintf ("btrfs", "RAID6\n");
+ nparities = 2;
+   }
+
+ /*
+  * A RAID 6 layout consists of several stripes spread on the
+  * disks, following a layout like the one below
+  *
+  *   Disk0  Disk1  Disk2  Disk3
+  *
+  *A1 B1 P1 Q1
+  *Q2 A2 B2 P2
+  *P3 Q3 A3 B3
+  *  [...]
+  *
+  *  Note that the placement of the parities depends on row index.
+  *  Pay attention that the BTRFS terminolgy may be different
+  *  from others RAID implementation (e.g. lvm/dm or md). In BTRFS
+  *  a contiguous block of data of a disk (like A1) is called
+  *  stripe.
+  *  In the code below:
+  *  - stripe_nr is the stripe number without the parities
+  *(A1 = 0, B1 = 1, A2 = 2, B2 = 3, ...),
+  *  - high is the row number (0 for A1...Q1, 1 for Q2...P2, ...),
+  *  - stripen is the disk number in a row (0 for A1,Q2,P3, 1 for
+  *B1...),
+  *  - off is the logical address to read,
+  *  - chunk_stripe_length is the size of a stripe (typically 64k),
+  *  - nstripes is the number of disks of a row
+  *  - low is the offset of the data inside a stripe,
+  *  - stripe_offset is the data offset in an array,
+  *  - csize is the "potential" data to read. It will be reduced to
+  *size if the latter is smaller.
+  *  - nparities is the number of parities (1 for RAID5, 2 for
+  *RAID6); used only in RAID5/6 code.
+  */
+ stripe_nr = grub_divmod64 (off, chunk_stripe_length, );
+
+ /*
+  * stripen is computed without the parities (0 for A1, A2, A3...
+  * 1 for B1, B2...).
+  */
+ high = grub_divmod64 (stripe_nr, nstripes - nparities, );
+
+ /*
+  * the stripes are spread across the disks, each row their
+  * position is shifted by 1 place. So to compute the real disk
+  * number occuped by a stripe, we need to sum also the
+  * "row number" in modulo nstripes (0 for A1, 1 for A2, 2 for
+  * A3).
+  */
+ grub_divmod64 (high + stripen, nstripes, );
+
+ stripe_offset = low + chunk_stripe_length * high;
+ csize = chunk_stripe_length - low;
+
  break;
}
  default:
-- 
2.19.0


___
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel


[PATCH V8] Add support for BTRFS raid5/6 to GRUB

2018-09-27 Thread Goffredo Baroncelli


i All,

the aim of this patches set is to provide support for a BTRFS raid5/6
filesystem in GRUB.

The first patch, implements the basic support for raid5/6. I.e this works when
all the disks are present.

The next 5 patches, are preparatory ones.

The 7th patch implements the raid5 recovery for btrfs (i.e. handling the
disappearing of 1 disk).
The 8th patch makes the code for handling the raid6 recovery more generic.
The last one implements the raid6 recovery for btrfs (i.e. handling the
disappearing up to two disks).

I tested the code in grub-emu, and it works both with all the disks,
and with some disks missing. I checked the crc32 calculated from grub and
from linux and these matched. Finally I checked if the support for md raid6
still works properly, and it does (with all drives and with up to 2 drives
missing)

Comments are welcome.

Changelog
v1: initial support for btrfs raid5/6. No recovery allowed
v2: full support for btrfs raid5/6. Recovery allowed
v3: some minor cleanup suggested by Daniel Kiper; reusing the
original raid6 recovery code of grub
v4: Several spell fix; better description of the RAID layout
in btrfs, and the variables which describes the stripe
positioning; split the patch #5 in two (#5 and #6)
v5: Several spell fix; improved code comment in patch #1, small
clean up in the code
v6: Small cleanup; improved the wording in the RAID6 layout
description; in the function raid6_recover_read_buffer() avoid
a unnecessary memcpy in case of invalid data;
v7: - patch 2,3,5,6,8 received an Review-by Daniel, and were unchanged from
the last time (only minor cleanup in the commit description requested by
Daniel)
- patch 7 received some small update rearranging a for(), and some
bracket around if()
- patch 4, received an update message which explains better why NULL
is stored in data->devices_attached[]
- patch 9, received a blank line to separate better a code line from
a previous comment. A description of 'parities_pos' was added
- patch 1, received a major update about the variable meaning description
in the comment. However I suspect that we need some further review to reach
a fully agreement about this text. NB: the update are relate only to 
comments
v8: - patch 2,5,6,8 received an Review-by Daniel, and were unchanged from
the last time (only minor cleanup in the commit description requested by
Daniel)
- patch 1 received some adjustement to the variables description due to
  the different terminology between BTRFS and other RAID implementatio.
  Added a description for the "nparities" variable.
- patch 3 removed some unnecessary curly brackets (change request by Daniel)
- patch 4 received an improved commit description about why and how 
  the function find_device() is changed
- patch 7 received an update which transforms a i = 0; while(i..) i++; in
  for( i = 0. ; i++);
- patch 9 received an update to the comment

BR
G.Baroncelli

--
gpg @keyserver.linux.it: Goffredo Baroncelli 
Key fingerprint BBF5 1610 0B64 DAC6 5F7D  17B2 0EDA 9B37 8B82 E0B5



___
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel


Re: [PATCH 1/9] btrfs: Add support for reading a filesystem with a RAID 5 or RAID 6 profile.

2018-09-26 Thread Goffredo Baroncelli
On 25/09/2018 17.31, Daniel Kiper wrote:
> On Wed, Sep 19, 2018 at 08:40:32PM +0200, Goffredo Baroncelli wrote:
>> From: Goffredo Baroncelli 
>>
>> Signed-off-by: Goffredo Baroncelli 
>> ---
>>  grub-core/fs/btrfs.c | 66 
>>  1 file changed, 66 insertions(+)
>>
>> diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
>> index be195448d..56c42746d 100644
>> --- a/grub-core/fs/btrfs.c
>> +++ b/grub-core/fs/btrfs.c
>> @@ -119,6 +119,8 @@ struct grub_btrfs_chunk_item
>>  #define GRUB_BTRFS_CHUNK_TYPE_RAID1 0x10
>>  #define GRUB_BTRFS_CHUNK_TYPE_DUPLICATED0x20
>>  #define GRUB_BTRFS_CHUNK_TYPE_RAID100x40
>> +#define GRUB_BTRFS_CHUNK_TYPE_RAID5 0x80
>> +#define GRUB_BTRFS_CHUNK_TYPE_RAID6 0x100
>>grub_uint8_t dummy2[0xc];
>>grub_uint16_t nstripes;
>>grub_uint16_t nsubstripes;
>> @@ -764,6 +766,70 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, 
>> grub_disk_addr_t addr,
>>stripe_offset = low + chunk_stripe_length
>>  * high;
>>csize = chunk_stripe_length - low;
>> +  break;
>> +}
>> +  case GRUB_BTRFS_CHUNK_TYPE_RAID5:
>> +  case GRUB_BTRFS_CHUNK_TYPE_RAID6:
>> +{
>> +  grub_uint64_t nparities, block_nr, high, low;
>> +
>> +  redundancy = 1;   /* no redundancy for now */
>> +
>> +  if (grub_le_to_cpu64 (chunk->type) & GRUB_BTRFS_CHUNK_TYPE_RAID5)
>> +{
>> +  grub_dprintf ("btrfs", "RAID5\n");
>> +  nparities = 1;
>> +}
>> +  else
>> +{
>> +  grub_dprintf ("btrfs", "RAID6\n");
>> +  nparities = 2;
>> +}
>> +
>> +  /*
>> +   * A RAID 6 layout consists of several blocks spread on the disks.
>> +   * The raid terminology is used to call all the blocks of a row
>> +   * "stripe". Unfortunately the BTRFS terminology confuses block
> 
> Stripe is data set or parity (parity stripe) on one disk. Block has
> different meaning. Please stick to btrfs terminology and say it clearly
> in the comment. And even add a link to btrfs wiki page to ease reading.
> 
> I think about this one:
>   
> https://btrfs.wiki.kernel.org/index.php/Manpage/mkfs.btrfs#BLOCK_GROUPS.2C_CHUNKS.2C_RAID
> 
>> +   * and stripe.
> 
> I do not think so. Or at least not so much...

Trust me, generally speaking stripe is the "row" in the disks (without the 
parity); looking at the ext3 man page:


   stride=stride-size
  Configure  the  filesystem  for  a  RAID  array with
  stride-size filesystem blocks. This is the number of
  blocks  read or written to disk before moving to the
  next disk, which is sometimes  referred  to  as  the
  chunk   size.   This  mostly  affects  placement  of
  filesystem metadata like bitmaps at mke2fs  time  to
  avoid  placing them on a single disk, which can hurt
  performance.  It may also be used by the block allo‐
  cator.

   stripe_width=stripe-width
  Configure  the  filesystem  for  a  RAID  array with
  stripe-width filesystem blocks per stripe.  This  is
  typically  stride-size * N, where N is the number of
  data-bearing disks in the  RAID  (e.g.  for  RAID  5
  there is one parity disk, so N will be the number of
  disks in the array minus 1).  This allows the  block
  allocator to prevent read-modify-write of the parity
  in a RAID stripe if possible when the data is  writ‐
  ten.


Looking at the RAID5 wikipedia page, it seems that the term "stripe" is 
coherent with the ext3 man page.

I suspect that the confusion is due to the fact that in RAID1 a stripe is in 
ONE disk (because the others are like parities). In BTRFS the RAID5/6 code uses 
the structure of RAID1 with some minimal extensions...

To be clear, I don't have problem to be adherent to the BTRFS terminology. 
However I found this very confusing because I was used to a different 
terminology. I am bit worried about the fact that grub uses both MD/DM code and 
BTRFS code; a quick look to the code (eg ./grub-core/disk/diskfilter.c) sh

Re: [PATCH 9/9] btrfs: Add RAID 6 recovery for a btrfs filesystem.

2018-09-26 Thread Goffredo Baroncelli
On 25/09/2018 21.20, Daniel Kiper wrote:
> On Wed, Sep 19, 2018 at 08:40:40PM +0200, Goffredo Baroncelli wrote:
>> From: Goffredo Baroncelli 
>>
[]
>> *  - stripe_offset is the disk offset,
>> *  - csize is the "potential" data to read. It will be reduced to
>> *size if the latter is smaller.
>> +   *  - parities_pos is the position of the parity inside a row (
> 
> s/inside/in/> 
>> +   *2 for P1, 3 for P2...)

+  *  - nparities is the number of parities (1 for RAID5, 2 for 
RAID6);
+  *used only in RAID5/6 code.

>> */
>>block_nr = grub_divmod64 (off, chunk_stripe_length, );
>>
>> @@ -1030,6 +1069,9 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, 
>> grub_disk_addr_t addr,
>> */
>>grub_divmod64 (high + stripen, nstripes, );
>>
>> +  grub_divmod64 (high + nstripes - nparities, nstripes,
>> + _pos);
> 
> I think that this math requires a bit of explanation in the comment
> before grub_divmod64(). Especially I am interested in why high +
> nstripes - nparities works as expected.


What about

/*
 * parities_pos is equal to "(high - nparities) % nstripes" (see the diagram 
above).
 * However "high - nparities" might be negative (eg when high == 0) leading to 
an
 * incorrect computation.
 * Instead "high + nstripes - nparities" is always positive and in modulo 
nstripes is
 * equal to "(high - nparities) % nstripes
 */
> 
> Daniel
> 
BR
G.Baroncelli

-- 
gpg @keyserver.linux.it: Goffredo Baroncelli 
Key fingerprint BBF5 1610 0B64 DAC6 5F7D  17B2 0EDA 9B37 8B82 E0B5

___
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel


[PATCH 7/9] btrfs: Add support for recovery for a RAID 5 btrfs profiles.

2018-09-26 Thread Goffredo Baroncelli
On 25/09/2018 21.10, Daniel Kiper wrote:
> On Wed, Sep 19, 2018 at 08:40:38PM +0200, Goffredo Baroncelli wrote:
>> From: Goffredo Baroncelli 
>>
>> Add support for recovery for a RAID 5 btrfs profile. In addition
>> it is added some code as preparatory work for RAID 6 recovery code.
>>
>> Signed-off-by: Goffredo Baroncelli 
>> ---
>>  grub-core/fs/btrfs.c | 169 +--
>>  1 file changed, 164 insertions(+), 5 deletions(-)
>>
>> diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
>> index 5c1ebae77..55a7eeffc 100644
>> --- a/grub-core/fs/btrfs.c
>> +++ b/grub-core/fs/btrfs.c
>> @@ -29,6 +29,7 @@
>>  #include 
>>  #include 
>>  #include 
>> +#include 
>>
>>  GRUB_MOD_LICENSE ("GPLv3+");
>>
>> @@ -665,6 +666,148 @@ btrfs_read_from_chunk (struct grub_btrfs_data *data,
>>  return err;
>>  }
>>
>> +struct raid56_buffer {
>> +  void *buf;
>> +  int  data_is_valid;
>> +};
>> +
>> +static void
>> +rebuild_raid5 (char *dest, struct raid56_buffer *buffers,
>> +   grub_uint64_t nstripes, grub_uint64_t csize)
>> +{
>> +  grub_uint64_t i;
>> +  int first;
>> +
>> +  i = 0;
>> +  while (buffers[i].data_is_valid && i < nstripes)
>> +++i;
> 
> for (i = 0; buffers[i].data_is_valid && i < nstripes; i++);
> 
>> +  if (i == nstripes)
>> +{
>> +  grub_dprintf ("btrfs", "called rebuild_raid5(), but all disks are 
>> OK\n");
>> +  return;
>> +}
>> +
>> +  grub_dprintf ("btrfs", "rebuilding RAID 5 stripe #%" PRIuGRUB_UINT64_T 
>> "\n",
>> +i);
> 
> One line here please.
> 
>> +  for (i = 0, first = 1; i < nstripes; i++)
>> +{
>> +  if (!buffers[i].data_is_valid)
>> +continue;
>> +
>> +  if (first) {
>> +grub_memcpy(dest, buffers[i].buf, csize);
>> +first = 0;
>> +  } else
>> +grub_crypto_xor (dest, dest, buffers[i].buf, csize);
>> +
>> +}
> 
> Hmmm... I think that this function can be simpler. You can drop first
> while/for and "if (i == nstripes)". Then here:
> 
> if (first) {
>   grub_dprintf ("btrfs", "called rebuild_raid5(), but all disks are OK\n");
> 
> Am I right?

Ehm.. no. The "if" is an internal check to avoid BUG. rebuild_raid5() should be 
called only if some disk is missed.
To perform this control, the code checks if all buffers are valid. Otherwise 
there is an internal BUG.

Checking "first" is a completely different test.

>> +}
>> +
>> +static grub_err_t
>> +raid56_read_retry (struct grub_btrfs_data *data,
>> +   struct grub_btrfs_chunk_item *chunk,
>> +   grub_uint64_t stripe_offset,
>> +   grub_uint64_t csize, void *buf)
>> +{
>> +  struct raid56_buffer *buffers;
>> +  grub_uint64_t nstripes = grub_le_to_cpu16 (chunk->nstripes);
>> +  grub_uint64_t chunk_type = grub_le_to_cpu64 (chunk->type);
>> +  grub_err_t ret = GRUB_ERR_NONE;
> 
> s/GRUB_ERR_NONE/GRUB_ERR_OUT_OF_MEMORY/ and then you can drop at
> least two relevant assigments and some curly brackets. Of course
> before cleanup label you have to add ret = GRUB_ERR_NONE.
> 
>> +  grub_uint64_t i, failed_devices;
>> +
>> +  buffers = grub_zalloc (sizeof(*buffers) * nstripes);
>> +  if (!buffers)
>> +{
>> +  ret = GRUB_ERR_OUT_OF_MEMORY;
>> +  goto cleanup;
>> +}
>> +
>> +  for (i = 0; i < nstripes; i++)
>> +{
>> +  buffers[i].buf = grub_zalloc (csize);
>> +  if (!buffers[i].buf)
>> +{
>> +  ret = GRUB_ERR_OUT_OF_MEMORY;
>> +  goto cleanup;
>> +}
>> +}
>> +
>> +  for (failed_devices = 0, i = 0; i < nstripes; i++)
>> +{
>> +  struct grub_btrfs_chunk_stripe *stripe;
>> +  grub_disk_addr_t paddr;
>> +  grub_device_t dev;
>> +  grub_err_t err2;
> 
> s/err2/err/?

Ok

> 
>> +
>> +  stripe = (struct grub_btrfs_chunk_stripe *) (chunk + 1);
>> +  stripe += i;
> 
> Why not stripe = ((struct grub_btrfs_chunk_stripe *) (chunk + 1)) + i;?

Make sense
> 
> Daniel
> 


-- 
gpg @keyserver.linux.it: Goffredo Baroncelli 
Key fingerprint BBF5 1610 0B64 DAC6 5F7D  17B2 0EDA 9B37 8B82 E0B5



___
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel


[PATCH 4/9] btrfs: Avoid a rescan for a device which was already not found.

2018-09-26 Thread Goffredo Baroncelli
On 25/09/2018 19.29, Daniel Kiper wrote:
> On Wed, Sep 19, 2018 at 08:40:35PM +0200, Goffredo Baroncelli wrote:
>> From: Goffredo Baroncelli 
>>
>> If a device is not found, do not return immediately but
>> record this failure by storing NULL in data->devices_attached[].
> 
> Still the same question: Where the store happens in the code?
> I cannot find it in the patch below. This have to be clarified.
> 
> Daniel


What about the following commit description
-
Change the behavior of find_device(): before the patch, a read of a missed 
device might trigger a rescan. However, it is never recorded that a device is 
missed, so each single read of a missed device might triggers a rescan.
It is the caller who decides if a rescan is performed in case of a missed 
device. And it does quite often, without considering if in the past a devices 
was already found as "missed"
This behavior causes a lot of unneeded rescan, causing a huge slowdown in case 
of a missed device.

After the patch, the "missed device" information is stored in the cache (as a 
NULL value). A rescan is triggered only if no information at all is found in 
the cache. This means that only the first time a read of a missed device 
triggers a rescan.

The change in the code is done removing "return NULL" when the disk is not 
found. So it is always executed the code which stores in the cache the value 
returned by grub_device_iterate(): NULL if the device is missed, or a valid 
data otherwise.
-
> 
>> This way we avoid unnecessary devices rescan, and speedup the
>> reads in case of a degraded array.
>>
>> Signed-off-by: Goffredo Baroncelli 
>> ---
>>  grub-core/fs/btrfs.c | 19 +--
>>  1 file changed, 9 insertions(+), 10 deletions(-)
>>
>> diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
>> index 0cdfaf7c0..6e42c33f6 100644
>> --- a/grub-core/fs/btrfs.c
>> +++ b/grub-core/fs/btrfs.c
>> @@ -588,7 +588,7 @@ find_device_iter (const char *name, void *data)
>>  }
>>
>>  static grub_device_t
>> -find_device (struct grub_btrfs_data *data, grub_uint64_t id, int do_rescan)
>> +find_device (struct grub_btrfs_data *data, grub_uint64_t id)
>>  {
>>struct find_device_ctx ctx = {
>>  .data = data,
>> @@ -600,12 +600,9 @@ find_device (struct grub_btrfs_data *data, 
>> grub_uint64_t id, int do_rescan)
>>for (i = 0; i < data->n_devices_attached; i++)
>>  if (id == data->devices_attached[i].id)
>>return data->devices_attached[i].dev;
>> -  if (do_rescan)
>> -grub_device_iterate (find_device_iter, );
>> -  if (!ctx.dev_found)
>> -{
>> -  return NULL;
>> -}
>> +
>> +  grub_device_iterate (find_device_iter, );
>> +
>>data->n_devices_attached++;
>>if (data->n_devices_attached > data->n_devices_allocated)
>>  {
>> @@ -617,7 +614,8 @@ find_device (struct grub_btrfs_data *data, grub_uint64_t 
>> id, int do_rescan)
>>  * sizeof (data->devices_attached[0]));
>>if (!data->devices_attached)
>>  {
>> -  grub_device_close (ctx.dev_found);
>> +  if (ctx.dev_found)
>> +grub_device_close (ctx.dev_found);
>>data->devices_attached = tmp;
>>return NULL;
>>  }
>> @@ -892,7 +890,7 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, 
>> grub_disk_addr_t addr,
>>" for laddr 0x%" PRIxGRUB_UINT64_T "\n", paddr,
>>addr);
>>
>> -dev = find_device (data, stripe->device_id, j);
>> +dev = find_device (data, stripe->device_id);
>>  if (!dev)
>>{
>>  grub_dprintf ("btrfs",
>> @@ -969,7 +967,8 @@ grub_btrfs_unmount (struct grub_btrfs_data *data)
>>unsigned i;
>>/* The device 0 is closed one layer upper.  */
>>for (i = 1; i < data->n_devices_attached; i++)
>> -grub_device_close (data->devices_attached[i].dev);
>> +if (data->devices_attached[i].dev)
>> +grub_device_close (data->devices_attached[i].dev);
>>grub_free (data->devices_attached);
>>grub_free (data->extent);
>>grub_free (data);
>> --
>> 2.19.0
>>
> 


-- 
gpg @keyserver.linux.it: Goffredo Baroncelli 
Key fingerprint BBF5 1610 0B64 DAC6 5F7D  17B2 0EDA 9B37 8B82 E0B5



___
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel


Re: [PATCH 1/9] btrfs: Add support for reading a filesystem with a RAID 5 or RAID 6 profile.

2018-09-19 Thread Goffredo Baroncelli
Please ignore this email

On 19/09/2018 20.36, Goffredo Baroncelli wrote:
> From: Goffredo Baroncelli 
> 
> Signed-off-by: Goffredo Baroncelli 
> ---
>  grub-core/fs/btrfs.c | 66 
>  1 file changed, 66 insertions(+)
> 
> diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
> index be195448d..56c42746d 100644
> --- a/grub-core/fs/btrfs.c
> +++ b/grub-core/fs/btrfs.c
> @@ -119,6 +119,8 @@ struct grub_btrfs_chunk_item
>  #define GRUB_BTRFS_CHUNK_TYPE_RAID1 0x10
>  #define GRUB_BTRFS_CHUNK_TYPE_DUPLICATED0x20
>  #define GRUB_BTRFS_CHUNK_TYPE_RAID100x40
> +#define GRUB_BTRFS_CHUNK_TYPE_RAID5 0x80
> +#define GRUB_BTRFS_CHUNK_TYPE_RAID6 0x100
>grub_uint8_t dummy2[0xc];
>grub_uint16_t nstripes;
>grub_uint16_t nsubstripes;
> @@ -764,6 +766,70 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, 
> grub_disk_addr_t addr,
> stripe_offset = low + chunk_stripe_length
>   * high;
> csize = chunk_stripe_length - low;
> +   break;
> + }
> +   case GRUB_BTRFS_CHUNK_TYPE_RAID5:
> +   case GRUB_BTRFS_CHUNK_TYPE_RAID6:
> + {
> +   grub_uint64_t nparities, block_nr, high, low;
> +
> +   redundancy = 1;   /* no redundancy for now */
> +
> +   if (grub_le_to_cpu64 (chunk->type) & GRUB_BTRFS_CHUNK_TYPE_RAID5)
> + {
> +   grub_dprintf ("btrfs", "RAID5\n");
> +   nparities = 1;
> + }
> +   else
> + {
> +   grub_dprintf ("btrfs", "RAID6\n");
> +   nparities = 2;
> + }
> +
> +   /*
> +* A RAID 6 layout consists of several blocks spread on the disks.
> +* The raid terminology is used to call all the blocks of a row
> +* "stripe". Unfortunately the BTRFS terminology confuses block
> +* and stripe.
> +*
> +*   Disk0  Disk1  Disk2  Disk3
> +*
> +*A1 B1 P1 Q1
> +*Q2 A2 B2 P2
> +*P3 Q3 A3 B3
> +*  [...]
> +*
> +*  Note that the placement of the parities depends on row index.
> +*  In the code below:
> +*  - block_nr is the block number without the parities
> +*(A1 = 0, B1 = 1, A2 = 2, B2 = 3, ...),
> +*  - high is the row number (0 for A1...Q1, 1 for Q2...P2, ...),
> +*  - stripen is the disk number (0 for A1,Q2,P3, 1 for B1...),
> +*  - off is the logical address to read
> +*  - chunk_stripe_length is the size of a block (typically 64k),
> +*  - nstripes is the number of disks,
> +*  - low is the offset of the data inside a stripe,
> +*  - stripe_offset is the disk offset,
> +*  - csize is the "potential" data to read. It will be reduced to
> +*size if the latter is smaller.
> +*/
> +   block_nr = grub_divmod64 (off, chunk_stripe_length, );
> +
> +   /*
> +* stripen is computed without the parities (0 for A1, A2, A3...
> +* 1 for B1, B2...).
> +*/
> +   high = grub_divmod64 (block_nr, nstripes - nparities, );
> +
> +   /*
> +* stripen is recomputed considering the parities (0 for A1, 1 for
> +    * A2, 2 for A3).
> +*/
> +   grub_divmod64 (high + stripen, nstripes, );
> +
> +   stripe_offset = low + chunk_stripe_length * high;
> +   csize = chunk_stripe_length - low;
> +
> break;
>   }
> default:
> 


-- 
gpg @keyserver.linux.it: Goffredo Baroncelli 
Key fingerprint BBF5 1610 0B64 DAC6 5F7D  17B2 0EDA 9B37 8B82 E0B5

___
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel


[PATCH 8/9] btrfs: Make more generic the code for RAID 6 rebuilding

2018-09-19 Thread Goffredo Baroncelli
From: Goffredo Baroncelli 

The original code which handles the recovery of a RAID 6 disks array
assumes that all reads are multiple of 1 << GRUB_DISK_SECTOR_BITS and it
assumes that all the I/O is done via the struct grub_diskfilter_segment.
This is not true for the btrfs code. In order to reuse the native
grub_raid6_recover() code, it is modified to not call
grub_diskfilter_read_node() directly, but to call an handler passed
as an argument.

Signed-off-by: Goffredo Baroncelli 
Reviewed-by: Daniel Kiper 
---
 grub-core/disk/raid6_recover.c | 52 ++
 include/grub/diskfilter.h  |  9 ++
 2 files changed, 43 insertions(+), 18 deletions(-)

diff --git a/grub-core/disk/raid6_recover.c b/grub-core/disk/raid6_recover.c
index aa674f6ca..0cf691ddf 100644
--- a/grub-core/disk/raid6_recover.c
+++ b/grub-core/disk/raid6_recover.c
@@ -74,14 +74,26 @@ mod_255 (unsigned x)
 }
 
 static grub_err_t
-grub_raid6_recover (struct grub_diskfilter_segment *array, int disknr, int p,
-char *buf, grub_disk_addr_t sector, grub_size_t size)
+raid6_recover_read_node (void *data, int disknr,
+   grub_uint64_t sector,
+   void *buf, grub_size_t size)
+{
+struct grub_diskfilter_segment *array = data;
+
+return grub_diskfilter_read_node (>nodes[disknr],
+ (grub_disk_addr_t)sector,
+ size >> GRUB_DISK_SECTOR_BITS, buf);
+}
+
+grub_err_t
+grub_raid6_recover_gen (void *data, grub_uint64_t nstripes, int disknr, int p,
+   char *buf, grub_uint64_t sector, grub_size_t size,
+   int layout, raid_recover_read_t read_func)
 {
   int i, q, pos;
   int bad1 = -1, bad2 = -1;
   char *pbuf = 0, *qbuf = 0;
 
-  size <<= GRUB_DISK_SECTOR_BITS;
   pbuf = grub_zalloc (size);
   if (!pbuf)
 goto quit;
@@ -91,17 +103,17 @@ grub_raid6_recover (struct grub_diskfilter_segment *array, 
int disknr, int p,
 goto quit;
 
   q = p + 1;
-  if (q == (int) array->node_count)
+  if (q == (int) nstripes)
 q = 0;
 
   pos = q + 1;
-  if (pos == (int) array->node_count)
+  if (pos == (int) nstripes)
 pos = 0;
 
-  for (i = 0; i < (int) array->node_count - 2; i++)
+  for (i = 0; i < (int) nstripes - 2; i++)
 {
   int c;
-  if (array->layout & GRUB_RAID_LAYOUT_MUL_FROM_POS)
+  if (layout & GRUB_RAID_LAYOUT_MUL_FROM_POS)
c = pos;
   else
c = i;
@@ -109,8 +121,7 @@ grub_raid6_recover (struct grub_diskfilter_segment *array, 
int disknr, int p,
 bad1 = c;
   else
 {
-  if (! grub_diskfilter_read_node (>nodes[pos], sector,
-  size >> GRUB_DISK_SECTOR_BITS, buf))
+ if (!read_func(data, pos, sector, buf, size))
 {
   grub_crypto_xor (pbuf, pbuf, buf, size);
   grub_raid_block_mulx (c, buf, size);
@@ -128,7 +139,7 @@ grub_raid6_recover (struct grub_diskfilter_segment *array, 
int disknr, int p,
 }
 
   pos++;
-  if (pos == (int) array->node_count)
+  if (pos == (int) nstripes)
 pos = 0;
 }
 
@@ -139,16 +150,14 @@ grub_raid6_recover (struct grub_diskfilter_segment 
*array, int disknr, int p,
   if (bad2 < 0)
 {
   /* One bad device */
-  if ((! grub_diskfilter_read_node (>nodes[p], sector,
-   size >> GRUB_DISK_SECTOR_BITS, buf)))
+  if (!read_func(data, p, sector, buf, size))
 {
   grub_crypto_xor (buf, buf, pbuf, size);
   goto quit;
 }
 
   grub_errno = GRUB_ERR_NONE;
-  if (grub_diskfilter_read_node (>nodes[q], sector,
-size >> GRUB_DISK_SECTOR_BITS, buf))
+  if (read_func(data, q, sector, buf, size))
 goto quit;
 
   grub_crypto_xor (buf, buf, qbuf, size);
@@ -160,14 +169,12 @@ grub_raid6_recover (struct grub_diskfilter_segment 
*array, int disknr, int p,
   /* Two bad devices */
   unsigned c;
 
-  if (grub_diskfilter_read_node (>nodes[p], sector,
-size >> GRUB_DISK_SECTOR_BITS, buf))
+  if (read_func(data, p, sector, buf, size))
 goto quit;
 
   grub_crypto_xor (pbuf, pbuf, buf, size);
 
-  if (grub_diskfilter_read_node (>nodes[q], sector,
-size >> GRUB_DISK_SECTOR_BITS, buf))
+  if (read_func(data, q, sector, buf, size))
 goto quit;
 
   grub_crypto_xor (qbuf, qbuf, buf, size);
@@ -190,6 +197,15 @@ quit:
   return grub_errno;
 }
 
+static grub_err_t
+grub_raid6_recover (struct grub_diskfilter_segment *array, int disknr, int p,
+char *buf, grub_disk_addr_t sector, grub_size_t size)
+{
+  return grub_raid6_recover_gen (array, array->n

[PATCH 4/9] btrfs: Avoid a rescan for a device which was already not found.

2018-09-19 Thread Goffredo Baroncelli
From: Goffredo Baroncelli 

If a device is not found, do not return immediately but
record this failure by storing NULL in data->devices_attached[].
This way we avoid unnecessary devices rescan, and speedup the
reads in case of a degraded array.

Signed-off-by: Goffredo Baroncelli 
---
 grub-core/fs/btrfs.c | 19 +--
 1 file changed, 9 insertions(+), 10 deletions(-)

diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
index 0cdfaf7c0..6e42c33f6 100644
--- a/grub-core/fs/btrfs.c
+++ b/grub-core/fs/btrfs.c
@@ -588,7 +588,7 @@ find_device_iter (const char *name, void *data)
 }
 
 static grub_device_t
-find_device (struct grub_btrfs_data *data, grub_uint64_t id, int do_rescan)
+find_device (struct grub_btrfs_data *data, grub_uint64_t id)
 {
   struct find_device_ctx ctx = {
 .data = data,
@@ -600,12 +600,9 @@ find_device (struct grub_btrfs_data *data, grub_uint64_t 
id, int do_rescan)
   for (i = 0; i < data->n_devices_attached; i++)
 if (id == data->devices_attached[i].id)
   return data->devices_attached[i].dev;
-  if (do_rescan)
-grub_device_iterate (find_device_iter, );
-  if (!ctx.dev_found)
-{
-  return NULL;
-}
+
+  grub_device_iterate (find_device_iter, );
+
   data->n_devices_attached++;
   if (data->n_devices_attached > data->n_devices_allocated)
 {
@@ -617,7 +614,8 @@ find_device (struct grub_btrfs_data *data, grub_uint64_t 
id, int do_rescan)
* sizeof (data->devices_attached[0]));
   if (!data->devices_attached)
{
- grub_device_close (ctx.dev_found);
+ if (ctx.dev_found)
+   grub_device_close (ctx.dev_found);
  data->devices_attached = tmp;
  return NULL;
}
@@ -892,7 +890,7 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, 
grub_disk_addr_t addr,
  " for laddr 0x%" PRIxGRUB_UINT64_T "\n", paddr,
  addr);
 
-   dev = find_device (data, stripe->device_id, j);
+   dev = find_device (data, stripe->device_id);
if (!dev)
  {
grub_dprintf ("btrfs",
@@ -969,7 +967,8 @@ grub_btrfs_unmount (struct grub_btrfs_data *data)
   unsigned i;
   /* The device 0 is closed one layer upper.  */
   for (i = 1; i < data->n_devices_attached; i++)
-grub_device_close (data->devices_attached[i].dev);
+if (data->devices_attached[i].dev)
+grub_device_close (data->devices_attached[i].dev);
   grub_free (data->devices_attached);
   grub_free (data->extent);
   grub_free (data);
-- 
2.19.0


___
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel


[PATCH 7/9] btrfs: Add support for recovery for a RAID 5 btrfs profiles.

2018-09-19 Thread Goffredo Baroncelli
From: Goffredo Baroncelli 

Add support for recovery for a RAID 5 btrfs profile. In addition
it is added some code as preparatory work for RAID 6 recovery code.

Signed-off-by: Goffredo Baroncelli 
---
 grub-core/fs/btrfs.c | 169 +--
 1 file changed, 164 insertions(+), 5 deletions(-)

diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
index 5c1ebae77..55a7eeffc 100644
--- a/grub-core/fs/btrfs.c
+++ b/grub-core/fs/btrfs.c
@@ -29,6 +29,7 @@
 #include 
 #include 
 #include 
+#include 
 
 GRUB_MOD_LICENSE ("GPLv3+");
 
@@ -665,6 +666,148 @@ btrfs_read_from_chunk (struct grub_btrfs_data *data,
 return err;
 }
 
+struct raid56_buffer {
+  void *buf;
+  int  data_is_valid;
+};
+
+static void
+rebuild_raid5 (char *dest, struct raid56_buffer *buffers,
+  grub_uint64_t nstripes, grub_uint64_t csize)
+{
+  grub_uint64_t i;
+  int first;
+
+  i = 0;
+  while (buffers[i].data_is_valid && i < nstripes)
+++i;
+
+  if (i == nstripes)
+{
+  grub_dprintf ("btrfs", "called rebuild_raid5(), but all disks are OK\n");
+  return;
+}
+
+  grub_dprintf ("btrfs", "rebuilding RAID 5 stripe #%" PRIuGRUB_UINT64_T "\n",
+   i);
+
+  for (i = 0, first = 1; i < nstripes; i++)
+{
+  if (!buffers[i].data_is_valid)
+   continue;
+
+  if (first) {
+   grub_memcpy(dest, buffers[i].buf, csize);
+   first = 0;
+  } else
+   grub_crypto_xor (dest, dest, buffers[i].buf, csize);
+
+}
+}
+
+static grub_err_t
+raid56_read_retry (struct grub_btrfs_data *data,
+  struct grub_btrfs_chunk_item *chunk,
+  grub_uint64_t stripe_offset,
+  grub_uint64_t csize, void *buf)
+{
+  struct raid56_buffer *buffers;
+  grub_uint64_t nstripes = grub_le_to_cpu16 (chunk->nstripes);
+  grub_uint64_t chunk_type = grub_le_to_cpu64 (chunk->type);
+  grub_err_t ret = GRUB_ERR_NONE;
+  grub_uint64_t i, failed_devices;
+
+  buffers = grub_zalloc (sizeof(*buffers) * nstripes);
+  if (!buffers)
+{
+  ret = GRUB_ERR_OUT_OF_MEMORY;
+  goto cleanup;
+}
+
+  for (i = 0; i < nstripes; i++)
+{
+  buffers[i].buf = grub_zalloc (csize);
+  if (!buffers[i].buf)
+   {
+ ret = GRUB_ERR_OUT_OF_MEMORY;
+ goto cleanup;
+   }
+}
+
+  for (failed_devices = 0, i = 0; i < nstripes; i++)
+{
+  struct grub_btrfs_chunk_stripe *stripe;
+  grub_disk_addr_t paddr;
+  grub_device_t dev;
+  grub_err_t err2;
+
+  stripe = (struct grub_btrfs_chunk_stripe *) (chunk + 1);
+  stripe += i;
+
+  paddr = grub_le_to_cpu64 (stripe->offset) + stripe_offset;
+  grub_dprintf ("btrfs", "reading paddr %" PRIxGRUB_UINT64_T
+" from stripe ID %" PRIxGRUB_UINT64_T "\n", paddr,
+stripe->device_id);
+
+  dev = find_device (data, stripe->device_id);
+  if (!dev)
+   {
+ buffers[i].data_is_valid = 0;
+ grub_dprintf ("btrfs", "stripe %" PRIuGRUB_UINT64_T " FAILED (dev ID 
%"
+   PRIxGRUB_UINT64_T ")\n", i, stripe->device_id);
+ failed_devices++;
+ continue;
+   }
+
+  err2 = grub_disk_read (dev->disk, paddr >> GRUB_DISK_SECTOR_BITS,
+paddr & (GRUB_DISK_SECTOR_SIZE - 1),
+csize, buffers[i].buf);
+  if (err2 == GRUB_ERR_NONE)
+   {
+ buffers[i].data_is_valid = 1;
+ grub_dprintf ("btrfs", "stripe %" PRIuGRUB_UINT64_T " Ok (dev ID %"
+   PRIxGRUB_UINT64_T ")\n", i, stripe->device_id);
+   }
+  else
+   {
+ buffers[i].data_is_valid = 0;
+ grub_dprintf ("btrfs", "stripe %" PRIuGRUB_UINT64_T
+   " FAILED (dev ID %" PRIxGRUB_UINT64_T ")\n", i,
+   stripe->device_id);
+ failed_devices++;
+   }
+}
+
+  if (failed_devices > 1 && (chunk_type & GRUB_BTRFS_CHUNK_TYPE_RAID5))
+{
+  grub_dprintf ("btrfs",
+   "not enough disks for RAID 5: total %" PRIuGRUB_UINT64_T
+   ", missing %" PRIuGRUB_UINT64_T "\n",
+   nstripes, failed_devices);
+  ret = GRUB_ERR_READ_ERROR;
+  goto cleanup;
+}
+  else
+grub_dprintf ("btrfs",
+ "enough disks for RAID 5 rebuilding: total %"
+ PRIuGRUB_UINT64_T ", missing %" PRIuGRUB_UINT64_T "\n",
+ nstripes, failed_devices);
+
+  /* if these are enough, try to rebuild the data */
+  if (chunk_type & GRUB_BTRFS_CHUNK_TYPE_RAID5)
+rebuild_raid5 (buf, buffers, nstripes, csiz

[PATCH 3/9] btrfs: Move the error logging from find_device() to its caller.

2018-09-19 Thread Goffredo Baroncelli
From: Goffredo Baroncelli 

The caller knows better if this error is fatal or not, i.e. another disk is
available or not.

This is a preparatory patch.

Signed-off-by: Goffredo Baroncelli 
Reviewed-by: Daniel Kiper 
---
 grub-core/fs/btrfs.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
index 4f404f4b2..0cdfaf7c0 100644
--- a/grub-core/fs/btrfs.c
+++ b/grub-core/fs/btrfs.c
@@ -604,9 +604,6 @@ find_device (struct grub_btrfs_data *data, grub_uint64_t 
id, int do_rescan)
 grub_device_iterate (find_device_iter, );
   if (!ctx.dev_found)
 {
-  grub_error (GRUB_ERR_BAD_FS,
- N_("couldn't find a necessary member device "
-"of multi-device filesystem"));
   return NULL;
 }
   data->n_devices_attached++;
@@ -898,6 +895,9 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, 
grub_disk_addr_t addr,
dev = find_device (data, stripe->device_id, j);
if (!dev)
  {
+   grub_dprintf ("btrfs",
+ "couldn't find a necessary member device "
+ "of multi-device filesystem\n");
err = grub_errno;
grub_errno = GRUB_ERR_NONE;
continue;
-- 
2.19.0


___
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel


[PATCH 2/9] btrfs: Add helper to check the btrfs header.

2018-09-19 Thread Goffredo Baroncelli
From: Goffredo Baroncelli 

This helper is used in a few places to help the debugging. As
conservative approach the error is only logged.
This does not impact the error handling.

Signed-off-by: Goffredo Baroncelli 
Reviewed-by: Daniel Kiper 
---
 grub-core/fs/btrfs.c | 24 +++-
 1 file changed, 23 insertions(+), 1 deletion(-)

diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
index 56c42746d..4f404f4b2 100644
--- a/grub-core/fs/btrfs.c
+++ b/grub-core/fs/btrfs.c
@@ -77,7 +77,8 @@ struct btrfs_header
 {
   grub_btrfs_checksum_t checksum;
   grub_btrfs_uuid_t uuid;
-  grub_uint8_t dummy[0x30];
+  grub_uint64_t bytenr;
+  grub_uint8_t dummy[0x28];
   grub_uint32_t nitems;
   grub_uint8_t level;
 } GRUB_PACKED;
@@ -286,6 +287,25 @@ free_iterator (struct grub_btrfs_leaf_descriptor *desc)
   grub_free (desc->data);
 }
 
+static grub_err_t
+check_btrfs_header (struct grub_btrfs_data *data, struct btrfs_header *header,
+grub_disk_addr_t addr)
+{
+  if (grub_le_to_cpu64 (header->bytenr) != addr)
+{
+  grub_dprintf ("btrfs", "btrfs_header.bytenr is not equal node addr\n");
+  return grub_error (GRUB_ERR_BAD_FS,
+"header bytenr is not equal node addr");
+}
+  if (grub_memcmp (data->sblock.uuid, header->uuid, sizeof(grub_btrfs_uuid_t)))
+{
+  grub_dprintf ("btrfs", "btrfs_header.uuid doesn't match sblock uuid\n");
+  return grub_error (GRUB_ERR_BAD_FS,
+"header uuid doesn't match sblock uuid");
+}
+  return GRUB_ERR_NONE;
+}
+
 static grub_err_t
 save_ref (struct grub_btrfs_leaf_descriptor *desc,
  grub_disk_addr_t addr, unsigned i, unsigned m, int l)
@@ -341,6 +361,7 @@ next (struct grub_btrfs_data *data,
 
   err = grub_btrfs_read_logical (data, grub_le_to_cpu64 (node.addr),
 , sizeof (head), 0);
+  check_btrfs_header (data, , grub_le_to_cpu64 (node.addr));
   if (err)
return -err;
 
@@ -402,6 +423,7 @@ lower_bound (struct grub_btrfs_data *data,
   /* FIXME: preread few nodes into buffer. */
   err = grub_btrfs_read_logical (data, addr, , sizeof (head),
 recursion_depth + 1);
+  check_btrfs_header (data, , addr);
   if (err)
return err;
   addr += sizeof (head);
-- 
2.19.0


___
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel


[PATCH 9/9] btrfs: Add RAID 6 recovery for a btrfs filesystem.

2018-09-19 Thread Goffredo Baroncelli
From: Goffredo Baroncelli 

Add the RAID 6 recovery, in order to use a RAID 6 filesystem even if some
disks (up to two) are missing. This code use the md RAID 6 code already
present in grub.

Signed-off-by: Goffredo Baroncelli 
---
 grub-core/fs/btrfs.c | 54 +++-
 1 file changed, 48 insertions(+), 6 deletions(-)

diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
index 55a7eeffc..400cd56b6 100644
--- a/grub-core/fs/btrfs.c
+++ b/grub-core/fs/btrfs.c
@@ -30,6 +30,7 @@
 #include 
 #include 
 #include 
+#include 
 
 GRUB_MOD_LICENSE ("GPLv3+");
 
@@ -705,11 +706,36 @@ rebuild_raid5 (char *dest, struct raid56_buffer *buffers,
 }
 }
 
+static grub_err_t
+raid6_recover_read_buffer (void *data, int disk_nr,
+  grub_uint64_t addr __attribute__ ((unused)),
+  void *dest, grub_size_t size)
+{
+struct raid56_buffer *buffers = data;
+
+if (!buffers[disk_nr].data_is_valid)
+   return grub_errno = GRUB_ERR_READ_ERROR;
+
+grub_memcpy(dest, buffers[disk_nr].buf, size);
+
+return grub_errno = GRUB_ERR_NONE;
+}
+
+static void
+rebuild_raid6 (struct raid56_buffer *buffers, grub_uint64_t nstripes,
+   grub_uint64_t csize, grub_uint64_t parities_pos, void *dest,
+   grub_uint64_t stripen)
+
+{
+  grub_raid6_recover_gen (buffers, nstripes, stripen, parities_pos,
+  dest, 0, csize, 0, raid6_recover_read_buffer);
+}
+
 static grub_err_t
 raid56_read_retry (struct grub_btrfs_data *data,
   struct grub_btrfs_chunk_item *chunk,
-  grub_uint64_t stripe_offset,
-  grub_uint64_t csize, void *buf)
+  grub_uint64_t stripe_offset, grub_uint64_t stripen,
+  grub_uint64_t csize, void *buf, grub_uint64_t parities_pos)
 {
   struct raid56_buffer *buffers;
   grub_uint64_t nstripes = grub_le_to_cpu16 (chunk->nstripes);
@@ -787,6 +813,15 @@ raid56_read_retry (struct grub_btrfs_data *data,
   ret = GRUB_ERR_READ_ERROR;
   goto cleanup;
 }
+  else if (failed_devices > 2 && (chunk_type & GRUB_BTRFS_CHUNK_TYPE_RAID6))
+{
+  grub_dprintf ("btrfs",
+   "not enough disks for raid6: total %" PRIuGRUB_UINT64_T
+   ", missing %" PRIuGRUB_UINT64_T "\n",
+   nstripes, failed_devices);
+  ret = GRUB_ERR_READ_ERROR;
+  goto cleanup;
+}
   else
 grub_dprintf ("btrfs",
  "enough disks for RAID 5 rebuilding: total %"
@@ -797,7 +832,7 @@ raid56_read_retry (struct grub_btrfs_data *data,
   if (chunk_type & GRUB_BTRFS_CHUNK_TYPE_RAID5)
 rebuild_raid5 (buf, buffers, nstripes, csize);
   else
-grub_dprintf ("btrfs", "called rebuild_raid6(), NOT IMPLEMENTED\n");
+rebuild_raid6 (buffers, nstripes, csize, parities_pos, buf, stripen);
 
  cleanup:
   if (buffers)
@@ -886,9 +921,11 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, 
grub_disk_addr_t addr,
unsigned redundancy = 1;
unsigned i, j;
int is_raid56;
+   grub_uint64_t parities_pos = 0;
 
-   is_raid56 = !!(grub_le_to_cpu64 (chunk->type) &
-  GRUB_BTRFS_CHUNK_TYPE_RAID5);
+is_raid56 = !!(grub_le_to_cpu64 (chunk->type) &
+  (GRUB_BTRFS_CHUNK_TYPE_RAID5 |
+   GRUB_BTRFS_CHUNK_TYPE_RAID6));
 
if (grub_le_to_cpu64 (chunk->size) <= off)
  {
@@ -1015,6 +1052,8 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, 
grub_disk_addr_t addr,
   *  - stripe_offset is the disk offset,
   *  - csize is the "potential" data to read. It will be reduced to
   *size if the latter is smaller.
+  *  - parities_pos is the position of the parity inside a row (
+  *2 for P1, 3 for P2...)
   */
  block_nr = grub_divmod64 (off, chunk_stripe_length, );
 
@@ -1030,6 +1069,9 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, 
grub_disk_addr_t addr,
   */
  grub_divmod64 (high + stripen, nstripes, );
 
+ grub_divmod64 (high + nstripes - nparities, nstripes,
+_pos);
+
  stripe_offset = low + chunk_stripe_length * high;
  csize = chunk_stripe_length - low;
 
@@ -1081,7 +1123,7 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, 
grub_disk_addr_t addr,
grub_errno = GRUB_ERR_NONE;
if (err != GRUB_ERR_NONE)
  err = raid56_read_retry (data, chunk, stripe_offset,
-  csize, buf);
+  stripen, csize, buf, parities_pos);
  }
if (err == GRUB_ERR_NONE)
  break;
-- 
2.19.0


___
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel


[PATCH 6/9] btrfs: Refactor the code that read from disk

2018-09-19 Thread Goffredo Baroncelli
From: Goffredo Baroncelli 

Move the code in charge to read the data from disk into a separate
function. This helps to separate the error handling logic (which depends on
the different raid profiles) from the read from disk logic.
Refactoring this code increases the general readability too.

This is a preparatory patch, to help the adding of the RAID 5/6 recovery
code.

Signed-off-by: Goffredo Baroncelli 
Reviewed-by: Daniel Kiper 
---
 grub-core/fs/btrfs.c | 75 ++--
 1 file changed, 44 insertions(+), 31 deletions(-)

diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
index ee134c167..5c1ebae77 100644
--- a/grub-core/fs/btrfs.c
+++ b/grub-core/fs/btrfs.c
@@ -625,6 +625,46 @@ find_device (struct grub_btrfs_data *data, grub_uint64_t 
id)
   return ctx.dev_found;
 }
 
+static grub_err_t
+btrfs_read_from_chunk (struct grub_btrfs_data *data,
+  struct grub_btrfs_chunk_item *chunk,
+  grub_uint64_t stripen, grub_uint64_t stripe_offset,
+  int redundancy, grub_uint64_t csize,
+  void *buf)
+{
+struct grub_btrfs_chunk_stripe *stripe;
+grub_disk_addr_t paddr;
+grub_device_t dev;
+grub_err_t err;
+
+stripe = (struct grub_btrfs_chunk_stripe *) (chunk + 1);
+/* Right now the redundancy handling is easy.
+   With RAID5-like it will be more difficult.  */
+stripe += stripen + redundancy;
+
+paddr = grub_le_to_cpu64 (stripe->offset) + stripe_offset;
+
+grub_dprintf ("btrfs", "stripe %" PRIxGRUB_UINT64_T
+ " maps to 0x%" PRIxGRUB_UINT64_T
+ ". Reading paddr 0x%" PRIxGRUB_UINT64_T "\n",
+ stripen, stripe->offset, paddr);
+
+dev = find_device (data, stripe->device_id);
+if (!dev)
+  {
+   grub_dprintf ("btrfs",
+ "couldn't find a necessary member device "
+ "of multi-device filesystem\n");
+   grub_errno = GRUB_ERR_NONE;
+   return GRUB_ERR_READ_ERROR;
+  }
+
+err = grub_disk_read (dev->disk, paddr >> GRUB_DISK_SECTOR_BITS,
+ paddr & (GRUB_DISK_SECTOR_SIZE - 1),
+ csize, buf);
+return err;
+}
+
 static grub_err_t
 grub_btrfs_read_logical (struct grub_btrfs_data *data, grub_disk_addr_t addr,
 void *buf, grub_size_t size, int recursion_depth)
@@ -638,7 +678,6 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, 
grub_disk_addr_t addr,
   grub_err_t err = 0;
   struct grub_btrfs_key key_out;
   int challoc = 0;
-  grub_device_t dev;
   struct grub_btrfs_key key_in;
   grub_size_t chsize;
   grub_disk_addr_t chaddr;
@@ -877,36 +916,10 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, 
grub_disk_addr_t addr,
 
for (i = 0; i < redundancy; i++)
  {
-   struct grub_btrfs_chunk_stripe *stripe;
-   grub_disk_addr_t paddr;
-
-   stripe = (struct grub_btrfs_chunk_stripe *) (chunk + 1);
-   /* Right now the redundancy handling is easy.
-  With RAID5-like it will be more difficult.  */
-   stripe += stripen + i;
-
-   paddr = grub_le_to_cpu64 (stripe->offset) + stripe_offset;
-
-   grub_dprintf ("btrfs", "stripe %" PRIxGRUB_UINT64_T
- " maps to 0x%" PRIxGRUB_UINT64_T "\n",
- stripen, stripe->offset);
-   grub_dprintf ("btrfs", "reading paddr 0x%" PRIxGRUB_UINT64_T
- "\n", paddr);
-
-   dev = find_device (data, stripe->device_id);
-   if (!dev)
- {
-   grub_dprintf ("btrfs",
- "couldn't find a necessary member device "
- "of multi-device filesystem\n");
-   err = grub_errno;
-   grub_errno = GRUB_ERR_NONE;
-   continue;
- }
-
-   err = grub_disk_read (dev->disk, paddr >> GRUB_DISK_SECTOR_BITS,
- paddr & (GRUB_DISK_SECTOR_SIZE - 1),
- csize, buf);
+   err = btrfs_read_from_chunk (data, chunk, stripen,
+stripe_offset,
+i, /* redundancy */
+csize, buf);
if (!err)
  break;
grub_errno = GRUB_ERR_NONE;
-- 
2.19.0


___
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel


[PATCH 5/9] btrfs: Move logging code in grub_btrfs_read_logical()

2018-09-19 Thread Goffredo Baroncelli
From: Goffredo Baroncelli 

A portion of the logging code is moved outside of internal for(;;). The part
that is left inside is the one which depends on the internal for(;;) index.

This is a preparatory patch. The next one will refactor the code inside
the for(;;) into an another function.

Signed-off-by: Goffredo Baroncelli 
Reviewed-by: Daniel Kiper 
---
 grub-core/fs/btrfs.c | 25 ++---
 1 file changed, 14 insertions(+), 11 deletions(-)

diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
index 6e42c33f6..ee134c167 100644
--- a/grub-core/fs/btrfs.c
+++ b/grub-core/fs/btrfs.c
@@ -863,6 +863,18 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, 
grub_disk_addr_t addr,
 
for (j = 0; j < 2; j++)
  {
+   grub_dprintf ("btrfs", "chunk 0x%" PRIxGRUB_UINT64_T
+ "+0x%" PRIxGRUB_UINT64_T
+ " (%d stripes (%d substripes) of %"
+ PRIxGRUB_UINT64_T ")\n",
+ grub_le_to_cpu64 (key->offset),
+ grub_le_to_cpu64 (chunk->size),
+ grub_le_to_cpu16 (chunk->nstripes),
+ grub_le_to_cpu16 (chunk->nsubstripes),
+ grub_le_to_cpu64 (chunk->stripe_length));
+   grub_dprintf ("btrfs", "reading laddr 0x%" PRIxGRUB_UINT64_T "\n",
+ addr);
+
for (i = 0; i < redundancy; i++)
  {
struct grub_btrfs_chunk_stripe *stripe;
@@ -875,20 +887,11 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, 
grub_disk_addr_t addr,
 
paddr = grub_le_to_cpu64 (stripe->offset) + stripe_offset;
 
-   grub_dprintf ("btrfs", "chunk 0x%" PRIxGRUB_UINT64_T
- "+0x%" PRIxGRUB_UINT64_T
- " (%d stripes (%d substripes) of %"
- PRIxGRUB_UINT64_T ") stripe %" PRIxGRUB_UINT64_T
+   grub_dprintf ("btrfs", "stripe %" PRIxGRUB_UINT64_T
  " maps to 0x%" PRIxGRUB_UINT64_T "\n",
- grub_le_to_cpu64 (key->offset),
- grub_le_to_cpu64 (chunk->size),
- grub_le_to_cpu16 (chunk->nstripes),
- grub_le_to_cpu16 (chunk->nsubstripes),
- grub_le_to_cpu64 (chunk->stripe_length),
  stripen, stripe->offset);
grub_dprintf ("btrfs", "reading paddr 0x%" PRIxGRUB_UINT64_T
- " for laddr 0x%" PRIxGRUB_UINT64_T "\n", paddr,
- addr);
+ "\n", paddr);
 
dev = find_device (data, stripe->device_id);
if (!dev)
-- 
2.19.0


___
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel


[PATCH 1/9] btrfs: Add support for reading a filesystem with a RAID 5 or RAID 6 profile.

2018-09-19 Thread Goffredo Baroncelli
From: Goffredo Baroncelli 

Signed-off-by: Goffredo Baroncelli 
---
 grub-core/fs/btrfs.c | 66 
 1 file changed, 66 insertions(+)

diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
index be195448d..56c42746d 100644
--- a/grub-core/fs/btrfs.c
+++ b/grub-core/fs/btrfs.c
@@ -119,6 +119,8 @@ struct grub_btrfs_chunk_item
 #define GRUB_BTRFS_CHUNK_TYPE_RAID1 0x10
 #define GRUB_BTRFS_CHUNK_TYPE_DUPLICATED0x20
 #define GRUB_BTRFS_CHUNK_TYPE_RAID100x40
+#define GRUB_BTRFS_CHUNK_TYPE_RAID5 0x80
+#define GRUB_BTRFS_CHUNK_TYPE_RAID6 0x100
   grub_uint8_t dummy2[0xc];
   grub_uint16_t nstripes;
   grub_uint16_t nsubstripes;
@@ -764,6 +766,70 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, 
grub_disk_addr_t addr,
  stripe_offset = low + chunk_stripe_length
* high;
  csize = chunk_stripe_length - low;
+ break;
+   }
+ case GRUB_BTRFS_CHUNK_TYPE_RAID5:
+ case GRUB_BTRFS_CHUNK_TYPE_RAID6:
+   {
+ grub_uint64_t nparities, block_nr, high, low;
+
+ redundancy = 1;   /* no redundancy for now */
+
+ if (grub_le_to_cpu64 (chunk->type) & GRUB_BTRFS_CHUNK_TYPE_RAID5)
+   {
+ grub_dprintf ("btrfs", "RAID5\n");
+ nparities = 1;
+   }
+ else
+   {
+ grub_dprintf ("btrfs", "RAID6\n");
+ nparities = 2;
+   }
+
+ /*
+  * A RAID 6 layout consists of several blocks spread on the disks.
+  * The raid terminology is used to call all the blocks of a row
+  * "stripe". Unfortunately the BTRFS terminology confuses block
+  * and stripe.
+  *
+  *   Disk0  Disk1  Disk2  Disk3
+  *
+  *A1 B1 P1 Q1
+  *Q2 A2 B2 P2
+  *P3 Q3 A3 B3
+  *  [...]
+  *
+  *  Note that the placement of the parities depends on row index.
+  *  In the code below:
+  *  - block_nr is the block number without the parities
+  *(A1 = 0, B1 = 1, A2 = 2, B2 = 3, ...),
+  *  - high is the row number (0 for A1...Q1, 1 for Q2...P2, ...),
+  *  - stripen is the disk number (0 for A1,Q2,P3, 1 for B1...),
+  *  - off is the logical address to read
+  *  - chunk_stripe_length is the size of a block (typically 64k),
+  *  - nstripes is the number of disks,
+  *  - low is the offset of the data inside a stripe,
+  *  - stripe_offset is the disk offset,
+  *  - csize is the "potential" data to read. It will be reduced to
+  *size if the latter is smaller.
+  */
+ block_nr = grub_divmod64 (off, chunk_stripe_length, );
+
+ /*
+  * stripen is computed without the parities (0 for A1, A2, A3...
+  * 1 for B1, B2...).
+  */
+ high = grub_divmod64 (block_nr, nstripes - nparities, );
+
+ /*
+  * stripen is recomputed considering the parities (0 for A1, 1 for
+  * A2, 2 for A3).
+  */
+ grub_divmod64 (high + stripen, nstripes, );
+
+ stripe_offset = low + chunk_stripe_length * high;
+ csize = chunk_stripe_length - low;
+
  break;
}
  default:
-- 
2.19.0


___
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel


[PATCH V7] Add support for BTRFS raid5/6 to GRUB

2018-09-19 Thread Goffredo Baroncelli





Hi All,

the aim of this patches set is to provide support for a BTRFS raid5/6
filesystem in GRUB.

The first patch, implements the basic support for raid5/6. I.e this works when
all the disks are present.

The next 5 patches, are preparatory ones.

The 7th patch implements the raid5 recovery for btrfs (i.e. handling the
disappearing of 1 disk).
The 8th patch makes the code for handling the raid6 recovery more generic.
The last one implements the raid6 recovery for btrfs (i.e. handling the
disappearing up to two disks).

I tested the code in grub-emu, and it works both with all the disks,
and with some disks missing. I checked the crc32 calculated from grub and
from linux and these matched. Finally I checked if the support for md raid6
still works properly, and it does (with all drives and with up to 2 drives
missing)

Comments are welcome.

Changelog
v1: initial support for btrfs raid5/6. No recovery allowed
v2: full support for btrfs raid5/6. Recovery allowed
v3: some minor cleanup suggested by Daniel Kiper; reusing the
original raid6 recovery code of grub
v4: Several spell fix; better description of the RAID layout
in btrfs, and the variables which describes the stripe
positioning; split the patch #5 in two (#5 and #6)
v5: Several spell fix; improved code comment in patch #1, small
clean up in the code
v6: Small cleanup; improved the wording in the RAID6 layout
description; in the function raid6_recover_read_buffer() avoid
a unnecessary memcpy in case of invalid data;
v7: - patch 2,3,5,6,8 received an Review-by Daniel, and were unchanged from
the last time (only minor cleanup in the commit description requested by
Daniel)
- patch 7 received some small update rearranging a for(), and some
bracket around if()
- patch 4, received an update message which explains better why NULL
is stored in data->devices_attached[]
- patch 9, received a blank line to separate better a code line from
a previous comment. A description of 'parities_pos' was added
- patch 1, received a major update about the variable meaning description
in the comment. However I suspect that we need some further review to reach
a fully agreement about this text. NB: the update are relate only to 
comments

BR
G.Baroncelli

--
gpg @keyserver.linux.it: Goffredo Baroncelli 
Key fingerprint BBF5 1610 0B64 DAC6 5F7D  17B2 0EDA 9B37 8B82 E0B5



___
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel


[PATCH 1/9] btrfs: Add support for reading a filesystem with a RAID 5 or RAID 6 profile.

2018-09-19 Thread Goffredo Baroncelli
From: Goffredo Baroncelli 

Signed-off-by: Goffredo Baroncelli 
---
 grub-core/fs/btrfs.c | 66 
 1 file changed, 66 insertions(+)

diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
index be195448d..56c42746d 100644
--- a/grub-core/fs/btrfs.c
+++ b/grub-core/fs/btrfs.c
@@ -119,6 +119,8 @@ struct grub_btrfs_chunk_item
 #define GRUB_BTRFS_CHUNK_TYPE_RAID1 0x10
 #define GRUB_BTRFS_CHUNK_TYPE_DUPLICATED0x20
 #define GRUB_BTRFS_CHUNK_TYPE_RAID100x40
+#define GRUB_BTRFS_CHUNK_TYPE_RAID5 0x80
+#define GRUB_BTRFS_CHUNK_TYPE_RAID6 0x100
   grub_uint8_t dummy2[0xc];
   grub_uint16_t nstripes;
   grub_uint16_t nsubstripes;
@@ -764,6 +766,70 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, 
grub_disk_addr_t addr,
  stripe_offset = low + chunk_stripe_length
* high;
  csize = chunk_stripe_length - low;
+ break;
+   }
+ case GRUB_BTRFS_CHUNK_TYPE_RAID5:
+ case GRUB_BTRFS_CHUNK_TYPE_RAID6:
+   {
+ grub_uint64_t nparities, block_nr, high, low;
+
+ redundancy = 1;   /* no redundancy for now */
+
+ if (grub_le_to_cpu64 (chunk->type) & GRUB_BTRFS_CHUNK_TYPE_RAID5)
+   {
+ grub_dprintf ("btrfs", "RAID5\n");
+ nparities = 1;
+   }
+ else
+   {
+ grub_dprintf ("btrfs", "RAID6\n");
+ nparities = 2;
+   }
+
+ /*
+  * A RAID 6 layout consists of several blocks spread on the disks.
+  * The raid terminology is used to call all the blocks of a row
+  * "stripe". Unfortunately the BTRFS terminology confuses block
+  * and stripe.
+  *
+  *   Disk0  Disk1  Disk2  Disk3
+  *
+  *A1 B1 P1 Q1
+  *Q2 A2 B2 P2
+  *P3 Q3 A3 B3
+  *  [...]
+  *
+  *  Note that the placement of the parities depends on row index.
+  *  In the code below:
+  *  - block_nr is the block number without the parities
+  *(A1 = 0, B1 = 1, A2 = 2, B2 = 3, ...),
+  *  - high is the row number (0 for A1...Q1, 1 for Q2...P2, ...),
+  *  - stripen is the disk number (0 for A1,Q2,P3, 1 for B1...),
+  *  - off is the logical address to read
+  *  - chunk_stripe_length is the size of a block (typically 64k),
+  *  - nstripes is the number of disks,
+  *  - low is the offset of the data inside a stripe,
+  *  - stripe_offset is the disk offset,
+  *  - csize is the "potential" data to read. It will be reduced to
+  *size if the latter is smaller.
+  */
+ block_nr = grub_divmod64 (off, chunk_stripe_length, );
+
+ /*
+  * stripen is computed without the parities (0 for A1, A2, A3...
+  * 1 for B1, B2...).
+  */
+ high = grub_divmod64 (block_nr, nstripes - nparities, );
+
+ /*
+  * stripen is recomputed considering the parities (0 for A1, 1 for
+  * A2, 2 for A3).
+  */
+ grub_divmod64 (high + stripen, nstripes, );
+
+ stripe_offset = low + chunk_stripe_length * high;
+ csize = chunk_stripe_length - low;
+
  break;
}
  default:
-- 
2.19.0


___
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel


[PATCH 4/9] btrfs: Avoid a rescan for a device which was already not found.

2018-07-18 Thread Goffredo Baroncelli
Resend because I forgot to put grub-devel in cc
-

On 07/12/2018 04:02 PM, Daniel Kiper wrote:
> On Tue, Jun 19, 2018 at 08:22:19PM +0200, Goffredo Baroncelli wrote:
>> Forward because this patch still doesn't reach the mailing list
> 
> Could you fix that somehow? It is confusing.

I don't know which could be the problem. The only idea which I have, is that
the patch #4 is the only one which was never changed; gmail prevent two equal 
emails to reach the inbox two times. I suspect that these two thing are related

> 
>> --
>>
>> If a device is not found, record this failure by storing NULL in
>> data->devices_attached[]. This way we avoid unnecessary devices rescan,
>> and speedup the reads in case of a degraded array.
>>
>> Signed-off-by: Goffredo Baroncelli 
>> ---
>>  grub-core/fs/btrfs.c | 19 +--
>>  1 file changed, 9 insertions(+), 10 deletions(-)
>>
>> diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
>> index 8d07e2d72..70bcb0fdc 100644
>> --- a/grub-core/fs/btrfs.c
>> +++ b/grub-core/fs/btrfs.c
>> @@ -588,7 +588,7 @@ find_device_iter (const char *name, void *data)
>>  }
>>
>>  static grub_device_t
>> -find_device (struct grub_btrfs_data *data, grub_uint64_t id, int do_rescan)
>> +find_device (struct grub_btrfs_data *data, grub_uint64_t id)
>>  {
>>struct find_device_ctx ctx = {
>>  .data = data,
>> @@ -600,12 +600,9 @@ find_device (struct grub_btrfs_data *data, 
>> grub_uint64_t id, int do_rescan)
>>for (i = 0; i < data->n_devices_attached; i++)
>>  if (id == data->devices_attached[i].id)
>>return data->devices_attached[i].dev;
>> -  if (do_rescan)
>> -grub_device_iterate (find_device_iter, );
>> -  if (!ctx.dev_found)
>> -{
>> -  return NULL;
>> -}
>> +
>> +  grub_device_iterate (find_device_iter, );
>> +

[...]

> 
> The commit message or code is wrong. NULL is never stored into
> data->devices_attached[]. Am I missing something?

The original code searches a device (using 'id' as key) in the array
data->devices_attached[]; if the device is found, the device info are 
returned.

Otherwise find_device() searches the device using grub_device_iterate().

If ctx.dev_found is NULL, the device is not found and the function returns NULL.
Otherwise find_device() stores the pair "ctx.dev_found" and "id"
in data->devices_attached[] array for further searches.
Finally find_device() returns the value found.

My patch removes the check: if the device is not found, find_devices() stores 
the pair "NULL" and "id". So data->devices_attached[] array acts as cache both 
for the "founded" and "not founded" devices.




> 
> Daniel
> 
> ___
> Grub-devel mailing list
> Grub-devel@gnu.org
> https://lists.gnu.org/mailman/listinfo/grub-devel
> 


-- 
gpg @keyserver.linux.it: Goffredo Baroncelli 
Key fingerprint BBF5 1610 0B64 DAC6 5F7D  17B2 0EDA 9B37 8B82 E0B5



___
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel


[PATCH 1/9] btrfs: Add support for reading a filesystem with a RAID 5 or RAID 6 profile.

2018-07-18 Thread Goffredo Baroncelli
On 07/12/2018 03:46 PM, Daniel Kiper wrote:
> On Tue, Jun 19, 2018 at 07:39:48PM +0200, Goffredo Baroncelli wrote:
>> Signed-off-by: Goffredo Baroncelli 
>> ---
>>  grub-core/fs/btrfs.c | 70 
>>  1 file changed, 70 insertions(+)
>>
>> diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
>> index be195448d..fbabaebbe 100644
>> --- a/grub-core/fs/btrfs.c
>> +++ b/grub-core/fs/btrfs.c
>> @@ -119,6 +119,8 @@ struct grub_btrfs_chunk_item
>>  #define GRUB_BTRFS_CHUNK_TYPE_RAID1 0x10
>>  #define GRUB_BTRFS_CHUNK_TYPE_DUPLICATED0x20
>>  #define GRUB_BTRFS_CHUNK_TYPE_RAID100x40
>> +#define GRUB_BTRFS_CHUNK_TYPE_RAID5 0x80
>> +#define GRUB_BTRFS_CHUNK_TYPE_RAID6 0x100
>>grub_uint8_t dummy2[0xc];
>>grub_uint16_t nstripes;
>>grub_uint16_t nsubstripes;
>> @@ -764,6 +766,74 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, 
>> grub_disk_addr_t addr,
>>stripe_offset = low + chunk_stripe_length
>>  * high;
>>csize = chunk_stripe_length - low;
>> +  break;
>> +}
>> +  case GRUB_BTRFS_CHUNK_TYPE_RAID5:
>> +  case GRUB_BTRFS_CHUNK_TYPE_RAID6:
>> +{
>> +  grub_uint64_t nparities, stripe_nr, high, low;
>> +
>> +  redundancy = 1;   /* no redundancy for now */
>> +
>> +  if (grub_le_to_cpu64 (chunk->type) & GRUB_BTRFS_CHUNK_TYPE_RAID5)
>> +{
>> +  grub_dprintf ("btrfs", "RAID5\n");
>> +  nparities = 1;
>> +}
>> +  else
>> +{
>> +  grub_dprintf ("btrfs", "RAID6\n");
>> +  nparities = 2;
>> +}
>> +
>> +  /*
>> +   * Below is an example of a RAID 6 layout and the meaning of the
>> +   * variables. The same applies to RAID 5. The only differences is
>> +   * that there is only one parity disk instead of two.
>> +   *
>> +   * A RAID 6 layout consists of several stripes spread
>> +   * on the disks, following a layout like the one below
>> +   *
>> +   *   Disk0  Disk1  Disk2  Ddisk3
> 
> s/Ddisk3/Disk3/
> 
>> +   *
>> +   *A1 B1 P1 Q1
>> +   *Q2 A2 B2 P2
>> +   *P3 Q3 A3 B3
>> +   *  [...]
>> +   *
>> +   *  Note that the placement of the parities depends on row index.
>> +   *  In the code below:
>> +   *  - stripe_nr is the stripe number not considering the parities
>> +   *(A1 = 0, B1 = 1, A2 = 2, B2 = 3, ...),
>> +   *  - high is the row number (0 for A1...Q1, 1 for Q2...P2, ...),
>> +   *  - stripen is the column number (or disk number),
>> +   *  - off is the logical address to read (from the beginning of
>> +   *the chunk space),
> 
> What do you mean by chunk?

Is a specific btrfs data structure (see 
https://btrfs.wiki.kernel.org/index.php/Manpage/mkfs.btrfs#BLOCK_GROUPS.2C_CHUNKS.2C_RAID).
 Most of the BTRFS internal structures (e.g. where the file data is stored) are 
in terms of *logical address*. At this level there is no concept of redundancy 
or raid nor disks. You can see the logical address as a flat space, large as 
the sum of the disks sizes (minus the space used by the parities or mirroring).

The "logical space" is divided in "slices" called chunk. A chunk typically has 
a size in terms of hundred of megabytes (IIRC up to 1GB)

A chunk (or block group) is a mapping. A chunk maps a "logical address" to a 
"physical address" on the disks.

You can think a chunk as a structure like:

u64 profile
u64 logical_start, logical_end
u64 disk1_id, disk1_start, disk1_end
u64 disk2_id, disk2_start, disk2_end
u64 disk3_id, disk3_start, disk3_end
[...]

In order to translate a "logical_address" to the pair of "disk" and "physical 
address:
- find the chunk which maps the logical address (looking to the 
"logical_start", "logical_end")
- then on the basis of "profile" value you can have the following cases:
profile == SINGLE:
is the simplest one: the logical address is mapped to the range
(disk1_start...disk1_end) of the disk "disk_id1"

profile == RAID1
like the previous one; however if t

Re: [PATCH 1/9] btrfs: Add support for reading a filesystem with a RAID 5 or RAID 6 profile.

2018-07-09 Thread Goffredo Baroncelli
On 07/09/2018 12:20 PM, Daniel Kiper wrote:
> On Sun, Jul 08, 2018 at 05:51:37PM +0200, Goffredo Baroncelli wrote:
>> A gentle ping.
> 
> Sorry for delay. I remember about this patchset but I am swamped with other 
> stuff.
> I will take a look at it this week.
> 
> Daniel
> 
Thanks for the feedback: I was worried that my emails were lost somewhere 
in/with the spams :-)

BR
G.Baroncelli

-- 
gpg @keyserver.linux.it: Goffredo Baroncelli 
Key fingerprint BBF5 1610 0B64 DAC6 5F7D  17B2 0EDA 9B37 8B82 E0B5

___
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel


Re: [PATCH 1/9] btrfs: Add support for reading a filesystem with a RAID 5 or RAID 6 profile.

2018-07-08 Thread Goffredo Baroncelli
A gentle ping.

BR
G.Baroncelli


On 06/19/2018 07:39 PM, Goffredo Baroncelli wrote:
> Signed-off-by: Goffredo Baroncelli 
> ---
>  grub-core/fs/btrfs.c | 70 
>  1 file changed, 70 insertions(+)
> 
> diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
> index be195448d..fbabaebbe 100644
> --- a/grub-core/fs/btrfs.c
> +++ b/grub-core/fs/btrfs.c
> @@ -119,6 +119,8 @@ struct grub_btrfs_chunk_item
>  #define GRUB_BTRFS_CHUNK_TYPE_RAID1 0x10
>  #define GRUB_BTRFS_CHUNK_TYPE_DUPLICATED0x20
>  #define GRUB_BTRFS_CHUNK_TYPE_RAID100x40
> +#define GRUB_BTRFS_CHUNK_TYPE_RAID5 0x80
> +#define GRUB_BTRFS_CHUNK_TYPE_RAID6 0x100
>grub_uint8_t dummy2[0xc];
>grub_uint16_t nstripes;
>grub_uint16_t nsubstripes;
> @@ -764,6 +766,74 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, 
> grub_disk_addr_t addr,
> stripe_offset = low + chunk_stripe_length
>   * high;
> csize = chunk_stripe_length - low;
> +   break;
> + }
> +   case GRUB_BTRFS_CHUNK_TYPE_RAID5:
> +   case GRUB_BTRFS_CHUNK_TYPE_RAID6:
> + {
> +   grub_uint64_t nparities, stripe_nr, high, low;
> +
> +   redundancy = 1;   /* no redundancy for now */
> +
> +   if (grub_le_to_cpu64 (chunk->type) & GRUB_BTRFS_CHUNK_TYPE_RAID5)
> + {
> +   grub_dprintf ("btrfs", "RAID5\n");
> +   nparities = 1;
> + }
> +   else
> + {
> +   grub_dprintf ("btrfs", "RAID6\n");
> +   nparities = 2;
> + }
> +
> +   /*
> +* Below is an example of a RAID 6 layout and the meaning of the
> +* variables. The same applies to RAID 5. The only differences is
> +* that there is only one parity disk instead of two.
> +*
> +* A RAID 6 layout consists of several stripes spread
> +* on the disks, following a layout like the one below
> +*
> +*   Disk0  Disk1  Disk2  Ddisk3
> +*
> +*A1 B1 P1 Q1
> +*Q2 A2 B2 P2
> +*P3 Q3 A3 B3
> +*  [...]
> +*
> +*  Note that the placement of the parities depends on row index.
> +*  In the code below:
> +*  - stripe_nr is the stripe number not considering the parities
> +*(A1 = 0, B1 = 1, A2 = 2, B2 = 3, ...),
> +*  - high is the row number (0 for A1...Q1, 1 for Q2...P2, ...),
> +*  - stripen is the column number (or disk number),
> +*  - off is the logical address to read (from the beginning of
> +*the chunk space),
> +*  - chunk_stripe_length is the size of a stripe (typically 64k),
> +*  - nstripes is the number of disks,
> +*  - low is the offset of the data inside a stripe,
> +*  - stripe_offset is the disk offset from the beginning
> +*of the disk chunk mapping start,
> +*  - csize is the "potential" data to read. It will be reduced to
> +*size if the latter is smaller.
> +*/
> +   stripe_nr = grub_divmod64 (off, chunk_stripe_length, );
> +
> +   /*
> +* stripen is evaluated without considering
> +* the parities (0 for A1, A2, A3... 1 for B1, B2...).
> +*/
> +   high = grub_divmod64 (stripe_nr, nstripes - nparities, );
> +
> +   /*
> +* stripen now considers also the parities (0 for A1, 1 for A2,
> +* 2 for A3). The math is performed modulo number of disks.
> +*/
> +   grub_divmod64 (high + stripen, nstripes, );
> +
> +   stripe_offset = low + chunk_stripe_length * high;
> +   csize = chunk_stripe_length - low;
> +
> break;
>   }
> default:
> 


-- 
gpg @keyserver.linux.it: Goffredo Baroncelli 
Key fingerprint BBF5 1610 0B64 DAC6 5F7D  17B2 0EDA 9B37 8B82 E0B5

___
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel


[PATCH 4/9] btrfs: Avoid a rescan for a device which was already not found.

2018-06-19 Thread Goffredo Baroncelli
Forward because this patch still doesn't reach the mailing list
--

If a device is not found, record this failure by storing NULL in
data->devices_attached[]. This way we avoid unnecessary devices rescan,
and speedup the reads in case of a degraded array.

Signed-off-by: Goffredo Baroncelli 
---
 grub-core/fs/btrfs.c | 19 +--
 1 file changed, 9 insertions(+), 10 deletions(-)

diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
index 8d07e2d72..70bcb0fdc 100644
--- a/grub-core/fs/btrfs.c
+++ b/grub-core/fs/btrfs.c
@@ -588,7 +588,7 @@ find_device_iter (const char *name, void *data)
 }
 
 static grub_device_t
-find_device (struct grub_btrfs_data *data, grub_uint64_t id, int do_rescan)
+find_device (struct grub_btrfs_data *data, grub_uint64_t id)
 {
   struct find_device_ctx ctx = {
 .data = data,
@@ -600,12 +600,9 @@ find_device (struct grub_btrfs_data *data, grub_uint64_t 
id, int do_rescan)
   for (i = 0; i < data->n_devices_attached; i++)
 if (id == data->devices_attached[i].id)
   return data->devices_attached[i].dev;
-  if (do_rescan)
-grub_device_iterate (find_device_iter, );
-  if (!ctx.dev_found)
-{
-  return NULL;
-}
+
+  grub_device_iterate (find_device_iter, );
+
   data->n_devices_attached++;
   if (data->n_devices_attached > data->n_devices_allocated)
 {
@@ -617,7 +614,8 @@ find_device (struct grub_btrfs_data *data, grub_uint64_t 
id, int do_rescan)
* sizeof (data->devices_attached[0]));
   if (!data->devices_attached)
{
- grub_device_close (ctx.dev_found);
+ if (ctx.dev_found)
+   grub_device_close (ctx.dev_found);
  data->devices_attached = tmp;
  return NULL;
}
@@ -896,7 +894,7 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, 
grub_disk_addr_t addr,
  " for laddr 0x%" PRIxGRUB_UINT64_T "\n", paddr,
  addr);
 
-   dev = find_device (data, stripe->device_id, j);
+   dev = find_device (data, stripe->device_id);
if (!dev)
  {
grub_dprintf ("btrfs",
@@ -973,7 +971,8 @@ grub_btrfs_unmount (struct grub_btrfs_data *data)
   unsigned i;
   /* The device 0 is closed one layer upper.  */
   for (i = 1; i < data->n_devices_attached; i++)
-grub_device_close (data->devices_attached[i].dev);
+if (data->devices_attached[i].dev)
+grub_device_close (data->devices_attached[i].dev);
   grub_free (data->devices_attached);
   grub_free (data->extent);
   grub_free (data);
-- 
2.18.0.rc2



___
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel


[PATCH 6/9] btrfs: Refactor the code that read from disk

2018-06-19 Thread Goffredo Baroncelli
Move the code in charge to read the data from disk into a separate
function. This helps to separate the error handling logic (which depend on
the different raid profiles) from the read from disk logic.
Refactoring this code increases the general readability too.

This is a preparatory patch, to help the adding of the RAID 5/6 recovery
code.

Signed-off-by: Goffredo Baroncelli 
---
 grub-core/fs/btrfs.c | 75 ++--
 1 file changed, 44 insertions(+), 31 deletions(-)

diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
index 15ed59151..fa5bf56e0 100644
--- a/grub-core/fs/btrfs.c
+++ b/grub-core/fs/btrfs.c
@@ -625,6 +625,46 @@ find_device (struct grub_btrfs_data *data, grub_uint64_t 
id)
   return ctx.dev_found;
 }
 
+static grub_err_t
+btrfs_read_from_chunk (struct grub_btrfs_data *data,
+  struct grub_btrfs_chunk_item *chunk,
+  grub_uint64_t stripen, grub_uint64_t stripe_offset,
+  int redundancy, grub_uint64_t csize,
+  void *buf)
+{
+struct grub_btrfs_chunk_stripe *stripe;
+grub_disk_addr_t paddr;
+grub_device_t dev;
+grub_err_t err;
+
+stripe = (struct grub_btrfs_chunk_stripe *) (chunk + 1);
+/* Right now the redundancy handling is easy.
+   With RAID5-like it will be more difficult.  */
+stripe += stripen + redundancy;
+
+paddr = grub_le_to_cpu64 (stripe->offset) + stripe_offset;
+
+grub_dprintf ("btrfs", "stripe %" PRIxGRUB_UINT64_T
+ " maps to 0x%" PRIxGRUB_UINT64_T
+ ". Reading paddr 0x%" PRIxGRUB_UINT64_T "\n",
+ stripen, stripe->offset, paddr);
+
+dev = find_device (data, stripe->device_id);
+if (!dev)
+  {
+   grub_dprintf ("btrfs",
+ "couldn't find a necessary member device "
+ "of multi-device filesystem\n");
+   grub_errno = GRUB_ERR_NONE;
+   return GRUB_ERR_READ_ERROR;
+  }
+
+err = grub_disk_read (dev->disk, paddr >> GRUB_DISK_SECTOR_BITS,
+ paddr & (GRUB_DISK_SECTOR_SIZE - 1),
+ csize, buf);
+return err;
+}
+
 static grub_err_t
 grub_btrfs_read_logical (struct grub_btrfs_data *data, grub_disk_addr_t addr,
 void *buf, grub_size_t size, int recursion_depth)
@@ -638,7 +678,6 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, 
grub_disk_addr_t addr,
   grub_err_t err = 0;
   struct grub_btrfs_key key_out;
   int challoc = 0;
-  grub_device_t dev;
   struct grub_btrfs_key key_in;
   grub_size_t chsize;
   grub_disk_addr_t chaddr;
@@ -881,36 +920,10 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, 
grub_disk_addr_t addr,
 
for (i = 0; i < redundancy; i++)
  {
-   struct grub_btrfs_chunk_stripe *stripe;
-   grub_disk_addr_t paddr;
-
-   stripe = (struct grub_btrfs_chunk_stripe *) (chunk + 1);
-   /* Right now the redundancy handling is easy.
-  With RAID5-like it will be more difficult.  */
-   stripe += stripen + i;
-
-   paddr = grub_le_to_cpu64 (stripe->offset) + stripe_offset;
-
-   grub_dprintf ("btrfs", "stripe %" PRIxGRUB_UINT64_T
- " maps to 0x%" PRIxGRUB_UINT64_T "\n",
- stripen, stripe->offset);
-   grub_dprintf ("btrfs", "reading paddr 0x%" PRIxGRUB_UINT64_T
- "\n", paddr);
-
-   dev = find_device (data, stripe->device_id);
-   if (!dev)
- {
-   grub_dprintf ("btrfs",
- "couldn't find a necessary member device "
- "of multi-device filesystem\n");
-   err = grub_errno;
-   grub_errno = GRUB_ERR_NONE;
-   continue;
- }
-
-   err = grub_disk_read (dev->disk, paddr >> GRUB_DISK_SECTOR_BITS,
- paddr & (GRUB_DISK_SECTOR_SIZE - 1),
- csize, buf);
+   err = btrfs_read_from_chunk (data, chunk, stripen,
+stripe_offset,
+i, /* redundancy */
+csize, buf);
if (!err)
  break;
grub_errno = GRUB_ERR_NONE;
-- 
2.18.0.rc2


___
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel


[PATCH 7/9] btrfs: Add support for recovery for a RAID 5 btrfs profiles.

2018-06-19 Thread Goffredo Baroncelli
Add support for recovery for a RAID 5 btrfs profile. In addition
it is added some code as preparatory work for RAID 6 recovery code.

Signed-off-by: Goffredo Baroncelli 
---
 grub-core/fs/btrfs.c | 177 +--
 1 file changed, 172 insertions(+), 5 deletions(-)

diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
index fa5bf56e0..7f01e447a 100644
--- a/grub-core/fs/btrfs.c
+++ b/grub-core/fs/btrfs.c
@@ -29,6 +29,7 @@
 #include 
 #include 
 #include 
+#include 
 
 GRUB_MOD_LICENSE ("GPLv3+");
 
@@ -665,6 +666,154 @@ btrfs_read_from_chunk (struct grub_btrfs_data *data,
 return err;
 }
 
+struct raid56_buffer {
+  void *buf;
+  int  data_is_valid;
+};
+
+static void
+rebuild_raid5 (char *dest, struct raid56_buffer *buffers,
+  grub_uint64_t nstripes, grub_uint64_t csize)
+{
+  grub_uint64_t i;
+  int first;
+
+  i = 0;
+  while (buffers[i].data_is_valid && i < nstripes)
+++i;
+
+  if (i == nstripes)
+{
+  grub_dprintf ("btrfs", "called rebuild_raid5(), but all disks are OK\n");
+  return;
+}
+
+  grub_dprintf ("btrfs", "rebuilding RAID 5 stripe #%" PRIuGRUB_UINT64_T "\n",
+   i);
+  first = 1;
+  for (i = 0; i < nstripes; i++)
+{
+  if (!buffers[i].data_is_valid)
+   continue;
+
+  if (first)
+   grub_memcpy(dest, buffers[i].buf, csize);
+  else
+   grub_crypto_xor (dest, dest, buffers[i].buf, csize);
+
+  first = 0;
+}
+}
+
+static grub_err_t
+raid56_read_retry (struct grub_btrfs_data *data,
+  struct grub_btrfs_chunk_item *chunk,
+  grub_uint64_t stripe_offset,
+  grub_uint64_t csize, void *buf)
+{
+  struct raid56_buffer *buffers = NULL;
+  grub_uint64_t nstripes = grub_le_to_cpu16 (chunk->nstripes);
+  grub_uint64_t chunk_type = grub_le_to_cpu64 (chunk->type);
+  grub_err_t ret = GRUB_ERR_NONE;
+  grub_uint64_t i, failed_devices;
+
+  buffers = grub_zalloc (sizeof(*buffers) * nstripes);
+  if (!buffers)
+{
+  ret = GRUB_ERR_OUT_OF_MEMORY;
+  goto cleanup;
+}
+
+  for (i = 0; i < nstripes; i++)
+{
+  buffers[i].buf = grub_zalloc (csize);
+  if (!buffers[i].buf)
+   {
+ ret = GRUB_ERR_OUT_OF_MEMORY;
+ goto cleanup;
+   }
+}
+
+  for (i = 0; i < nstripes; i++)
+{
+  struct grub_btrfs_chunk_stripe *stripe;
+  grub_disk_addr_t paddr;
+  grub_device_t dev;
+  grub_err_t err2;
+
+  stripe = (struct grub_btrfs_chunk_stripe *) (chunk + 1);
+  stripe += i;
+
+  paddr = grub_le_to_cpu64 (stripe->offset) + stripe_offset;
+  grub_dprintf ("btrfs", "reading paddr %" PRIxGRUB_UINT64_T
+" from stripe ID %" PRIxGRUB_UINT64_T "\n", paddr,
+stripe->device_id);
+
+  dev = find_device (data, stripe->device_id);
+  if (!dev)
+   {
+ buffers[i].data_is_valid = 0;
+ grub_dprintf ("btrfs", "stripe %" PRIuGRUB_UINT64_T " FAILED (dev ID 
%"
+   PRIxGRUB_UINT64_T ")\n", i, stripe->device_id);
+ continue;
+   }
+
+  err2 = grub_disk_read (dev->disk, paddr >> GRUB_DISK_SECTOR_BITS,
+paddr & (GRUB_DISK_SECTOR_SIZE - 1),
+csize, buffers[i].buf);
+  if (err2 == GRUB_ERR_NONE)
+   {
+ buffers[i].data_is_valid = 1;
+ grub_dprintf ("btrfs", "stripe %" PRIuGRUB_UINT64_T " Ok (dev ID %"
+   PRIxGRUB_UINT64_T ")\n", i, stripe->device_id);
+   }
+  else
+   {
+ buffers[i].data_is_valid = 0;
+ grub_dprintf ("btrfs", "stripe %" PRIuGRUB_UINT64_T
+   " FAILED (dev ID %" PRIxGRUB_UINT64_T ")\n", i,
+   stripe->device_id);
+   }
+}
+
+  for (failed_devices = i = 0; i < nstripes; i++)
+if (!buffers[i].data_is_valid)
+  ++failed_devices;
+  if (failed_devices > 1 && (chunk_type & GRUB_BTRFS_CHUNK_TYPE_RAID5))
+{
+  grub_dprintf ("btrfs",
+   "not enough disks for RAID 5: total %" PRIuGRUB_UINT64_T
+   ", missing %" PRIuGRUB_UINT64_T "\n",
+   nstripes, failed_devices);
+  ret = GRUB_ERR_READ_ERROR;
+  goto cleanup;
+}
+  else
+{
+  grub_dprintf ("btrfs",
+   "enough disks for RAID 5 rebuilding: total %"
+   PRIuGRUB_UINT64_T ", missing %" PRIuGRUB_UINT64_T "\n",
+   nstripes, failed_devices);
+}
+
+  /* if these are enough, try to rebuild the data */
+  if (chunk_type & GRUB_BTRFS_CHUNK_TYPE_RAID5)
+

[PATCH 3/9] btrfs: Move the error logging from find_device() to its caller.

2018-06-19 Thread Goffredo Baroncelli
The caller knows better if this error is fatal or not, i.e. another disk is
available or not.

This is a preparatory patch.

Signed-off-by: Goffredo Baroncelli 
---
 grub-core/fs/btrfs.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
index c1eb6c521..8d07e2d72 100644
--- a/grub-core/fs/btrfs.c
+++ b/grub-core/fs/btrfs.c
@@ -604,9 +604,6 @@ find_device (struct grub_btrfs_data *data, grub_uint64_t 
id, int do_rescan)
 grub_device_iterate (find_device_iter, );
   if (!ctx.dev_found)
 {
-  grub_error (GRUB_ERR_BAD_FS,
- N_("couldn't find a necessary member device "
-"of multi-device filesystem"));
   return NULL;
 }
   data->n_devices_attached++;
@@ -902,6 +899,9 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, 
grub_disk_addr_t addr,
dev = find_device (data, stripe->device_id, j);
if (!dev)
  {
+   grub_dprintf ("btrfs",
+ "couldn't find a necessary member device "
+ "of multi-device filesystem\n");
err = grub_errno;
grub_errno = GRUB_ERR_NONE;
continue;
-- 
2.18.0.rc2


___
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel


[PATCH 9/9] btrfs: Add RAID 6 recovery for a btrfs filesystem.

2018-06-19 Thread Goffredo Baroncelli
Add the RAID 6 recovery, in order to use a RAID 6 filesystem even if some
disks (up to two) are missing. This code use the md RAID 6 code already
present in grub.

Signed-off-by: Goffredo Baroncelli 
---
 grub-core/fs/btrfs.c | 51 ++--
 1 file changed, 45 insertions(+), 6 deletions(-)

diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
index 7f01e447a..13777c868 100644
--- a/grub-core/fs/btrfs.c
+++ b/grub-core/fs/btrfs.c
@@ -30,6 +30,7 @@
 #include 
 #include 
 #include 
+#include 
 
 GRUB_MOD_LICENSE ("GPLv3+");
 
@@ -705,11 +706,36 @@ rebuild_raid5 (char *dest, struct raid56_buffer *buffers,
 }
 }
 
+static grub_err_t
+raid6_recover_read_buffer (void *data, int disk_nr,
+  grub_uint64_t addr __attribute__ ((unused)),
+  void *dest, grub_size_t size)
+{
+struct raid56_buffer *buffers = data;
+
+if (!buffers[disk_nr].data_is_valid)
+   return grub_errno = GRUB_ERR_READ_ERROR;
+
+grub_memcpy(dest, buffers[disk_nr].buf, size);
+
+return grub_errno = GRUB_ERR_NONE;
+}
+
+static void
+rebuild_raid6 (struct raid56_buffer *buffers, grub_uint64_t nstripes,
+   grub_uint64_t csize, grub_uint64_t parities_pos, void *dest,
+   grub_uint64_t stripen)
+
+{
+  grub_raid6_recover_gen (buffers, nstripes, stripen, parities_pos,
+  dest, 0, csize, 0, raid6_recover_read_buffer);
+}
+
 static grub_err_t
 raid56_read_retry (struct grub_btrfs_data *data,
   struct grub_btrfs_chunk_item *chunk,
-  grub_uint64_t stripe_offset,
-  grub_uint64_t csize, void *buf)
+  grub_uint64_t stripe_offset, grub_uint64_t stripen,
+  grub_uint64_t csize, void *buf, grub_uint64_t parities_pos)
 {
   struct raid56_buffer *buffers = NULL;
   grub_uint64_t nstripes = grub_le_to_cpu16 (chunk->nstripes);
@@ -788,6 +814,15 @@ raid56_read_retry (struct grub_btrfs_data *data,
   ret = GRUB_ERR_READ_ERROR;
   goto cleanup;
 }
+  else if (failed_devices > 2 && (chunk_type & GRUB_BTRFS_CHUNK_TYPE_RAID6))
+{
+  grub_dprintf ("btrfs",
+   "not enough disks for raid6: total %" PRIuGRUB_UINT64_T
+   ", missing %" PRIuGRUB_UINT64_T "\n",
+   nstripes, failed_devices);
+  ret = GRUB_ERR_READ_ERROR;
+  goto cleanup;
+}
   else
 {
   grub_dprintf ("btrfs",
@@ -800,7 +835,7 @@ raid56_read_retry (struct grub_btrfs_data *data,
   if (chunk_type & GRUB_BTRFS_CHUNK_TYPE_RAID5)
 rebuild_raid5 (buf, buffers, nstripes, csize);
   else
-grub_dprintf ("btrfs", "called rebuild_raid6(), NOT IMPLEMENTED\n");
+rebuild_raid6 (buffers, nstripes, csize, parities_pos, buf, stripen);
 
  cleanup:
   if (buffers)
@@ -892,9 +927,11 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, 
grub_disk_addr_t addr,
unsigned redundancy = 1;
unsigned i, j;
int is_raid56;
+   grub_uint64_t parities_pos = 0;
 
-   is_raid56 = !!(grub_le_to_cpu64 (chunk->type) &
-  GRUB_BTRFS_CHUNK_TYPE_RAID5);
+is_raid56 = !!(grub_le_to_cpu64 (chunk->type) &
+  (GRUB_BTRFS_CHUNK_TYPE_RAID5 |
+   GRUB_BTRFS_CHUNK_TYPE_RAID6));
 
if (grub_le_to_cpu64 (chunk->size) <= off)
  {
@@ -1039,6 +1076,8 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, 
grub_disk_addr_t addr,
   * 2 for A3). The math is performed modulo number of disks.
   */
  grub_divmod64 (high + stripen, nstripes, );
+ grub_divmod64 (high + nstripes - nparities, nstripes,
+_pos);
 
  stripe_offset = low + chunk_stripe_length * high;
  csize = chunk_stripe_length - low;
@@ -1093,7 +1132,7 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, 
grub_disk_addr_t addr,
grub_errno = GRUB_ERR_NONE;
if (err != GRUB_ERR_NONE)
  err = raid56_read_retry (data, chunk, stripe_offset,
-  csize, buf);
+  stripen, csize, buf, parities_pos);
  }
if (err == GRUB_ERR_NONE)
  break;
-- 
2.18.0.rc2


___
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel


[PATCH 8/9] btrfs: Make more generic the code for RAID 6 rebuilding

2018-06-19 Thread Goffredo Baroncelli
The original code which handles the recovery of a RAID 6 disks array
assumes that all reads are multiple of 1 << GRUB_DISK_SECTOR_BITS and it
assumes that all the I/O is done via the struct grub_diskfilter_segment.
This is not true for the btrfs code. In order to reuse the native
grub_raid6_recover() code, it is modified to not call
grub_diskfilter_read_node() directly, but to call an handler passed
as an argument.

Signed-off-by: Goffredo Baroncelli 
---
 grub-core/disk/raid6_recover.c | 52 ++
 include/grub/diskfilter.h  |  9 ++
 2 files changed, 43 insertions(+), 18 deletions(-)

diff --git a/grub-core/disk/raid6_recover.c b/grub-core/disk/raid6_recover.c
index aa674f6ca..0cf691ddf 100644
--- a/grub-core/disk/raid6_recover.c
+++ b/grub-core/disk/raid6_recover.c
@@ -74,14 +74,26 @@ mod_255 (unsigned x)
 }
 
 static grub_err_t
-grub_raid6_recover (struct grub_diskfilter_segment *array, int disknr, int p,
-char *buf, grub_disk_addr_t sector, grub_size_t size)
+raid6_recover_read_node (void *data, int disknr,
+   grub_uint64_t sector,
+   void *buf, grub_size_t size)
+{
+struct grub_diskfilter_segment *array = data;
+
+return grub_diskfilter_read_node (>nodes[disknr],
+ (grub_disk_addr_t)sector,
+ size >> GRUB_DISK_SECTOR_BITS, buf);
+}
+
+grub_err_t
+grub_raid6_recover_gen (void *data, grub_uint64_t nstripes, int disknr, int p,
+   char *buf, grub_uint64_t sector, grub_size_t size,
+   int layout, raid_recover_read_t read_func)
 {
   int i, q, pos;
   int bad1 = -1, bad2 = -1;
   char *pbuf = 0, *qbuf = 0;
 
-  size <<= GRUB_DISK_SECTOR_BITS;
   pbuf = grub_zalloc (size);
   if (!pbuf)
 goto quit;
@@ -91,17 +103,17 @@ grub_raid6_recover (struct grub_diskfilter_segment *array, 
int disknr, int p,
 goto quit;
 
   q = p + 1;
-  if (q == (int) array->node_count)
+  if (q == (int) nstripes)
 q = 0;
 
   pos = q + 1;
-  if (pos == (int) array->node_count)
+  if (pos == (int) nstripes)
 pos = 0;
 
-  for (i = 0; i < (int) array->node_count - 2; i++)
+  for (i = 0; i < (int) nstripes - 2; i++)
 {
   int c;
-  if (array->layout & GRUB_RAID_LAYOUT_MUL_FROM_POS)
+  if (layout & GRUB_RAID_LAYOUT_MUL_FROM_POS)
c = pos;
   else
c = i;
@@ -109,8 +121,7 @@ grub_raid6_recover (struct grub_diskfilter_segment *array, 
int disknr, int p,
 bad1 = c;
   else
 {
-  if (! grub_diskfilter_read_node (>nodes[pos], sector,
-  size >> GRUB_DISK_SECTOR_BITS, buf))
+ if (!read_func(data, pos, sector, buf, size))
 {
   grub_crypto_xor (pbuf, pbuf, buf, size);
   grub_raid_block_mulx (c, buf, size);
@@ -128,7 +139,7 @@ grub_raid6_recover (struct grub_diskfilter_segment *array, 
int disknr, int p,
 }
 
   pos++;
-  if (pos == (int) array->node_count)
+  if (pos == (int) nstripes)
 pos = 0;
 }
 
@@ -139,16 +150,14 @@ grub_raid6_recover (struct grub_diskfilter_segment 
*array, int disknr, int p,
   if (bad2 < 0)
 {
   /* One bad device */
-  if ((! grub_diskfilter_read_node (>nodes[p], sector,
-   size >> GRUB_DISK_SECTOR_BITS, buf)))
+  if (!read_func(data, p, sector, buf, size))
 {
   grub_crypto_xor (buf, buf, pbuf, size);
   goto quit;
 }
 
   grub_errno = GRUB_ERR_NONE;
-  if (grub_diskfilter_read_node (>nodes[q], sector,
-size >> GRUB_DISK_SECTOR_BITS, buf))
+  if (read_func(data, q, sector, buf, size))
 goto quit;
 
   grub_crypto_xor (buf, buf, qbuf, size);
@@ -160,14 +169,12 @@ grub_raid6_recover (struct grub_diskfilter_segment 
*array, int disknr, int p,
   /* Two bad devices */
   unsigned c;
 
-  if (grub_diskfilter_read_node (>nodes[p], sector,
-size >> GRUB_DISK_SECTOR_BITS, buf))
+  if (read_func(data, p, sector, buf, size))
 goto quit;
 
   grub_crypto_xor (pbuf, pbuf, buf, size);
 
-  if (grub_diskfilter_read_node (>nodes[q], sector,
-size >> GRUB_DISK_SECTOR_BITS, buf))
+  if (read_func(data, q, sector, buf, size))
 goto quit;
 
   grub_crypto_xor (qbuf, qbuf, buf, size);
@@ -190,6 +197,15 @@ quit:
   return grub_errno;
 }
 
+static grub_err_t
+grub_raid6_recover (struct grub_diskfilter_segment *array, int disknr, int p,
+char *buf, grub_disk_addr_t sector, grub_size_t size)
+{
+  return grub_raid6_recover_gen (array, array->node_count, disknr, p, buf,
+sector, size 

[PATCH 1/9] btrfs: Add support for reading a filesystem with a RAID 5 or RAID 6 profile.

2018-06-19 Thread Goffredo Baroncelli
Signed-off-by: Goffredo Baroncelli 
---
 grub-core/fs/btrfs.c | 70 
 1 file changed, 70 insertions(+)

diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
index be195448d..fbabaebbe 100644
--- a/grub-core/fs/btrfs.c
+++ b/grub-core/fs/btrfs.c
@@ -119,6 +119,8 @@ struct grub_btrfs_chunk_item
 #define GRUB_BTRFS_CHUNK_TYPE_RAID1 0x10
 #define GRUB_BTRFS_CHUNK_TYPE_DUPLICATED0x20
 #define GRUB_BTRFS_CHUNK_TYPE_RAID100x40
+#define GRUB_BTRFS_CHUNK_TYPE_RAID5 0x80
+#define GRUB_BTRFS_CHUNK_TYPE_RAID6 0x100
   grub_uint8_t dummy2[0xc];
   grub_uint16_t nstripes;
   grub_uint16_t nsubstripes;
@@ -764,6 +766,74 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, 
grub_disk_addr_t addr,
  stripe_offset = low + chunk_stripe_length
* high;
  csize = chunk_stripe_length - low;
+ break;
+   }
+ case GRUB_BTRFS_CHUNK_TYPE_RAID5:
+ case GRUB_BTRFS_CHUNK_TYPE_RAID6:
+   {
+ grub_uint64_t nparities, stripe_nr, high, low;
+
+ redundancy = 1;   /* no redundancy for now */
+
+ if (grub_le_to_cpu64 (chunk->type) & GRUB_BTRFS_CHUNK_TYPE_RAID5)
+   {
+ grub_dprintf ("btrfs", "RAID5\n");
+ nparities = 1;
+   }
+ else
+   {
+ grub_dprintf ("btrfs", "RAID6\n");
+ nparities = 2;
+   }
+
+ /*
+  * Below is an example of a RAID 6 layout and the meaning of the
+  * variables. The same applies to RAID 5. The only differences is
+  * that there is only one parity disk instead of two.
+  *
+  * A RAID 6 layout consists of several stripes spread
+  * on the disks, following a layout like the one below
+  *
+  *   Disk0  Disk1  Disk2  Ddisk3
+  *
+  *A1 B1 P1 Q1
+  *Q2 A2 B2 P2
+  *P3 Q3 A3 B3
+  *  [...]
+  *
+  *  Note that the placement of the parities depends on row index.
+  *  In the code below:
+  *  - stripe_nr is the stripe number not considering the parities
+  *(A1 = 0, B1 = 1, A2 = 2, B2 = 3, ...),
+  *  - high is the row number (0 for A1...Q1, 1 for Q2...P2, ...),
+  *  - stripen is the column number (or disk number),
+  *  - off is the logical address to read (from the beginning of
+  *the chunk space),
+  *  - chunk_stripe_length is the size of a stripe (typically 64k),
+  *  - nstripes is the number of disks,
+  *  - low is the offset of the data inside a stripe,
+  *  - stripe_offset is the disk offset from the beginning
+  *of the disk chunk mapping start,
+  *  - csize is the "potential" data to read. It will be reduced to
+  *size if the latter is smaller.
+  */
+ stripe_nr = grub_divmod64 (off, chunk_stripe_length, );
+
+ /*
+  * stripen is evaluated without considering
+  * the parities (0 for A1, A2, A3... 1 for B1, B2...).
+  */
+ high = grub_divmod64 (stripe_nr, nstripes - nparities, );
+
+ /*
+  * stripen now considers also the parities (0 for A1, 1 for A2,
+  * 2 for A3). The math is performed modulo number of disks.
+  */
+ grub_divmod64 (high + stripen, nstripes, );
+
+ stripe_offset = low + chunk_stripe_length * high;
+ csize = chunk_stripe_length - low;
+
  break;
}
  default:
-- 
2.18.0.rc2


___
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel


[PATCH V6] Add support for BTRFS raid5/6 to GRUB

2018-06-19 Thread Goffredo Baroncelli



Hi All,

the aim of this patches set is to provide support for a BTRFS raid5/6
filesystem in GRUB.

The first patch, implements the basic support for raid5/6. I.e this works when
all the disks are present.

The next 5 patches, are preparatory ones.

The 7th patch implements the raid5 recovery for btrfs (i.e. handling the
disappearing of 1 disk).
The 8th patch makes the code for handling the raid6 recovery more generic.
The last one implements the raid6 recovery for btrfs (i.e. handling the
disappearing up to two disks).

I tested the code in grub-emu, and it works both with all the disks,
and with some disks missing. I checked the crc32 calculated from grub and
from linux and these matched. Finally I checked if the support for md raid6
still works properly, and it does (with all drives and with up to 2 drives
missing)

Comments are welcome.

Changelog
v1: initial support for btrfs raid5/6. No recovery allowed
v2: full support for btrfs raid5/6. Recovery allowed
v3: some minor cleanup suggested by Daniel Kiper; reusing the
original raid6 recovery code of grub
v4: Several spell fix; better description of the RAID layout
in btrfs, and the variables which describes the stripe
positioning; split the patch #5 in two (#5 and #6)
v5: Several spell fix; improved code comment in patch #1, small
clean up in the code
v6: Small cleanup; improved the wording in the RAID6 layout
description; in the function raid6_recover_read_buffer() avoid
a unnecessary memcpy in case of invalid data;


___
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel


[PATCH 5/9] btrfs: Move logging code in grub_btrfs_read_logical()

2018-06-19 Thread Goffredo Baroncelli
A portion of the logging code is moved outside of internal for(;;). The part
that is left inside is the one which depends on the internal for(;;) index.

This is a preparatory patch. The next one will refactor the code inside
the for(;;) into an another function.

Signed-off-by: Goffredo Baroncelli 
---
 grub-core/fs/btrfs.c | 25 ++---
 1 file changed, 14 insertions(+), 11 deletions(-)

diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
index 70bcb0fdc..15ed59151 100644
--- a/grub-core/fs/btrfs.c
+++ b/grub-core/fs/btrfs.c
@@ -867,6 +867,18 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, 
grub_disk_addr_t addr,
 
for (j = 0; j < 2; j++)
  {
+   grub_dprintf ("btrfs", "chunk 0x%" PRIxGRUB_UINT64_T
+ "+0x%" PRIxGRUB_UINT64_T
+ " (%d stripes (%d substripes) of %"
+ PRIxGRUB_UINT64_T ")\n",
+ grub_le_to_cpu64 (key->offset),
+ grub_le_to_cpu64 (chunk->size),
+ grub_le_to_cpu16 (chunk->nstripes),
+ grub_le_to_cpu16 (chunk->nsubstripes),
+ grub_le_to_cpu64 (chunk->stripe_length));
+   grub_dprintf ("btrfs", "reading laddr 0x%" PRIxGRUB_UINT64_T "\n",
+ addr);
+
for (i = 0; i < redundancy; i++)
  {
struct grub_btrfs_chunk_stripe *stripe;
@@ -879,20 +891,11 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, 
grub_disk_addr_t addr,
 
paddr = grub_le_to_cpu64 (stripe->offset) + stripe_offset;
 
-   grub_dprintf ("btrfs", "chunk 0x%" PRIxGRUB_UINT64_T
- "+0x%" PRIxGRUB_UINT64_T
- " (%d stripes (%d substripes) of %"
- PRIxGRUB_UINT64_T ") stripe %" PRIxGRUB_UINT64_T
+   grub_dprintf ("btrfs", "stripe %" PRIxGRUB_UINT64_T
  " maps to 0x%" PRIxGRUB_UINT64_T "\n",
- grub_le_to_cpu64 (key->offset),
- grub_le_to_cpu64 (chunk->size),
- grub_le_to_cpu16 (chunk->nstripes),
- grub_le_to_cpu16 (chunk->nsubstripes),
- grub_le_to_cpu64 (chunk->stripe_length),
  stripen, stripe->offset);
grub_dprintf ("btrfs", "reading paddr 0x%" PRIxGRUB_UINT64_T
- " for laddr 0x%" PRIxGRUB_UINT64_T "\n", paddr,
- addr);
+ "\n", paddr);
 
dev = find_device (data, stripe->device_id);
if (!dev)
-- 
2.18.0.rc2


___
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel


[PATCH 2/9] btrfs: Add helper to check the btrfs header.

2018-06-19 Thread Goffredo Baroncelli
This helper s used in a few places to help the debugging. As
conservative approach the error is only logged.
This to not impact the error handling.

Signed-off-by: Goffredo Baroncelli 
---
 grub-core/fs/btrfs.c | 24 +++-
 1 file changed, 23 insertions(+), 1 deletion(-)

diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
index fbabaebbe..c1eb6c521 100644
--- a/grub-core/fs/btrfs.c
+++ b/grub-core/fs/btrfs.c
@@ -77,7 +77,8 @@ struct btrfs_header
 {
   grub_btrfs_checksum_t checksum;
   grub_btrfs_uuid_t uuid;
-  grub_uint8_t dummy[0x30];
+  grub_uint64_t bytenr;
+  grub_uint8_t dummy[0x28];
   grub_uint32_t nitems;
   grub_uint8_t level;
 } GRUB_PACKED;
@@ -286,6 +287,25 @@ free_iterator (struct grub_btrfs_leaf_descriptor *desc)
   grub_free (desc->data);
 }
 
+static grub_err_t
+check_btrfs_header (struct grub_btrfs_data *data, struct btrfs_header *header,
+grub_disk_addr_t addr)
+{
+  if (grub_le_to_cpu64 (header->bytenr) != addr)
+{
+  grub_dprintf ("btrfs", "btrfs_header.bytenr is not equal node addr\n");
+  return grub_error (GRUB_ERR_BAD_FS,
+"header bytenr is not equal node addr");
+}
+  if (grub_memcmp (data->sblock.uuid, header->uuid, sizeof(grub_btrfs_uuid_t)))
+{
+  grub_dprintf ("btrfs", "btrfs_header.uuid doesn't match sblock uuid\n");
+  return grub_error (GRUB_ERR_BAD_FS,
+"header uuid doesn't match sblock uuid");
+}
+  return GRUB_ERR_NONE;
+}
+
 static grub_err_t
 save_ref (struct grub_btrfs_leaf_descriptor *desc,
  grub_disk_addr_t addr, unsigned i, unsigned m, int l)
@@ -341,6 +361,7 @@ next (struct grub_btrfs_data *data,
 
   err = grub_btrfs_read_logical (data, grub_le_to_cpu64 (node.addr),
 , sizeof (head), 0);
+  check_btrfs_header (data, , grub_le_to_cpu64 (node.addr));
   if (err)
return -err;
 
@@ -402,6 +423,7 @@ lower_bound (struct grub_btrfs_data *data,
   /* FIXME: preread few nodes into buffer. */
   err = grub_btrfs_read_logical (data, addr, , sizeof (head),
 recursion_depth + 1);
+  check_btrfs_header (data, , addr);
   if (err)
return err;
   addr += sizeof (head);
-- 
2.18.0.rc2


___
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel


Re: [PATCH 1/9] btrfs: Add support for reading a filesystem with a RAID 5 or RAID 6 profile.

2018-06-19 Thread Goffredo Baroncelli
On 06/14/2018 08:55 PM, Goffredo Baroncelli wrote:
>>> +  *  - stripe_offset is the offset from the beginning of the chunk
>>> +  *disks physical address,
>> I am not sure that I understand. Could clarify this?
> - stripe_offset is the offset (in bytes) from the beginning of the chunk 
> portion 
>   stored on disk.
> 
> You can think "stripe_offset" as the "row" in the drawing, but measured in 
> bytes.
> 

After some thoughts, I will update this description as:

-  *  - stripe_offset is the offset from the beginning of the chunk
-  *disks physical address,
+  *  - stripe_offset is the disk offset from the beginning
+  *    of the disk chunk mapping start,

-- 
gpg @keyserver.linux.it: Goffredo Baroncelli 
Key fingerprint BBF5 1610 0B64 DAC6 5F7D  17B2 0EDA 9B37 8B82 E0B5

___
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel


Re: [PATCH 7/9] btrfs: Add support for recovery for a RAID 5 btrfs profiles.

2018-06-18 Thread Goffredo Baroncelli
On 06/14/2018 09:05 PM, Goffredo Baroncelli wrote:
>>> +
>>> +cleanup:
>> Space before the label please.
>> I have asked about earlier.
> The line before the label is already a space; Am I missing something 

I think that now I understand: are you requiring a space before the label *on 
the same line* ?
I found this style in some grub code, but this is not used everywhere (ntfs.c 
and nilfs dont have the space, but xfs has it ).

-- 
gpg @keyserver.linux.it: Goffredo Baroncelli 
Key fingerprint BBF5 1610 0B64 DAC6 5F7D  17B2 0EDA 9B37 8B82 E0B5

___
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel


Re: [PATCH 7/9] btrfs: Add support for recovery for a RAID 5 btrfs profiles.

2018-06-14 Thread Goffredo Baroncelli
On 06/14/2018 03:03 PM, Daniel Kiper wrote:
> On Sun, Jun 03, 2018 at 08:53:46PM +0200, Goffredo Baroncelli wrote:
>> Add support for recovery fo a RAID 5 btrfs profile. In addition
> 
> s/fo /for /
> 
>> it is added some code as preparatory work for RAID 6 recovery code.
>>
>> Signed-off-by: Goffredo Baroncelli 
>> ---
>>  grub-core/fs/btrfs.c | 180 +--
>>  1 file changed, 175 insertions(+), 5 deletions(-)
>>
>> diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
>> index 9cdbfe792..c8f034641 100644
>> --- a/grub-core/fs/btrfs.c
>> +++ b/grub-core/fs/btrfs.c
>> @@ -29,6 +29,7 @@
>>  #include 
>>  #include 
>>  #include 
>> +#include 
>>
>>  GRUB_MOD_LICENSE ("GPLv3+");
>>
>> @@ -666,6 +667,157 @@ btrfs_read_from_chunk (struct grub_btrfs_data *data,
>>  return err;
>>  }
>>
>> +struct raid56_buffer {
>> +  void *buf;
>> +  int  data_is_valid;
>> +};
>> +
>> +static void
>> +rebuild_raid5 (char *dest, struct raid56_buffer *buffers,
>> +   grub_uint64_t nstripes, grub_uint64_t csize)
>> +{
>> +  grub_uint64_t i;
> 
> grub_uint64_t i = 0;
> int first = 1;
> 
>> +  int first;
>> +
>> +  i = 0;
> 
> Then you can drop this assignment.

I prefer to tie the assignment to the place where it is used.

> 
>> +  while (buffers[i].data_is_valid && i < nstripes)
>> +++i;
>> +
>> +  if (i == nstripes)
>> +{
>> +  grub_dprintf ("btrfs", "called rebuild_raid5(), but all disks are 
>> OK\n");
>> +  return;
>> +}
>> +
>> +  grub_dprintf ("btrfs", "rebuilding RAID 5 stripe #%" PRIuGRUB_UINT64_T 
>> "\n",
>> +i);
> 
> This can be in one line.
> 
>> +  first = 1;
> 
> And you can drop this assignment too.
> 
>> +  for (i = 0; i < nstripes; i++)
>> +{
>> +  if (!buffers[i].data_is_valid)
>> +continue;
>> +
>> +  if (first)
>> +grub_memcpy(dest, buffers[i].buf, csize);
>> +  else
>> +grub_crypto_xor (dest, dest, buffers[i].buf, csize);
> 
> I am not sure why at first you use grub_memcpy() and
> then move to grub_crypto_xor(). Could you explain this?
> Why do not use grub_crypto_xor() in all cases?

This avoid to require that dest has to be initialized to zero.

> 
>> +
>> +  first = 0;
>> +}
>> +}
>> +
>> +static grub_err_t
>> +raid56_read_retry (struct grub_btrfs_data *data,
>> +   struct grub_btrfs_chunk_item *chunk,
>> +   grub_uint64_t stripe_offset,
>> +   grub_uint64_t csize, void *buf)
>> +{
>> +
> 
> Please drop this empty line. I have asked about that earlier.
> 
>> +  struct raid56_buffer *buffers = NULL;
>> +  grub_uint64_t nstripes = grub_le_to_cpu16 (chunk->nstripes);
>> +  grub_uint64_t chunk_type = grub_le_to_cpu64 (chunk->type);
>> +  grub_err_t ret = GRUB_ERR_NONE;
>> +  grub_uint64_t i, failed_devices;
>> +
>> +  buffers = grub_zalloc (sizeof(*buffers) * nstripes);
> 
> How often this function is called? Maybe you should consider
> doing memory allocation for this function only once and free
> it at btrfs module unload.

This is only needed in case of recovery. Which should happen no too often. 
Usually, this function is never called.
> 
>> +  if (!buffers)
>> +{
>> +  ret = GRUB_ERR_OUT_OF_MEMORY;
>> +  goto cleanup;
>> +}
>> +
>> +  for (i = 0; i < nstripes; i++)
>> +{
>> +  buffers[i].buf = grub_zalloc (csize);
> 
> Ditto.
> 
>> +  if (!buffers[i].buf)
>> +{
>> +  ret = GRUB_ERR_OUT_OF_MEMORY;
>> +  goto cleanup;
>> +}
>> +}
>> +
>> +  for (i = 0; i < nstripes; i++)
>> +{
>> +  struct grub_btrfs_chunk_stripe *stripe;
>> +  grub_disk_addr_t paddr;
>> +  grub_device_t dev;
>> +  grub_err_t err2;
>> +
>> +  stripe = (struct grub_btrfs_chunk_stripe *) (chunk + 1);
>> +  stripe += i;
>> +
>> +  paddr = grub_le_to_cpu64 (stripe->offset) + stripe_offset;
>> +  grub_dprintf ("btrfs", "reading paddr %" PRIxGRUB_UINT64_T
>> +" from stripe ID %" PRIxGRUB_UINT64_T "\n", paddr,
>> +stripe->device_id);
>> +
>> +  dev = find_device (data, stripe->device_id)

Re: [PATCH 1/9] btrfs: Add support for reading a filesystem with a RAID 5 or RAID 6 profile.

2018-06-14 Thread Goffredo Baroncelli
On 06/14/2018 01:17 PM, Daniel Kiper wrote:
> On Sun, Jun 03, 2018 at 08:53:40PM +0200, Goffredo Baroncelli wrote:
>> Signed-off-by: Goffredo Baroncelli 
>> ---
>>  grub-core/fs/btrfs.c | 70 
>>  1 file changed, 70 insertions(+)
>>
>> diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
>> index be195448d..4d418859b 100644
>> --- a/grub-core/fs/btrfs.c
>> +++ b/grub-core/fs/btrfs.c
>> @@ -119,6 +119,8 @@ struct grub_btrfs_chunk_item
>>  #define GRUB_BTRFS_CHUNK_TYPE_RAID1 0x10
>>  #define GRUB_BTRFS_CHUNK_TYPE_DUPLICATED0x20
>>  #define GRUB_BTRFS_CHUNK_TYPE_RAID100x40
>> +#define GRUB_BTRFS_CHUNK_TYPE_RAID5 0x80
>> +#define GRUB_BTRFS_CHUNK_TYPE_RAID6 0x100
>>grub_uint8_t dummy2[0xc];
>>grub_uint16_t nstripes;
>>grub_uint16_t nsubstripes;
>> @@ -764,6 +766,74 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, 
>> grub_disk_addr_t addr,
>>stripe_offset = low + chunk_stripe_length
>>  * high;
>>csize = chunk_stripe_length - low;
>> +  break;
>> +}
>> +  case GRUB_BTRFS_CHUNK_TYPE_RAID5:
>> +  case GRUB_BTRFS_CHUNK_TYPE_RAID6:
>> +{
>> +  grub_uint64_t nparities, stripe_nr, high, low;
>> +
>> +  redundancy = 1;   /* no redundancy for now */
>> +
>> +  if (grub_le_to_cpu64 (chunk->type) & GRUB_BTRFS_CHUNK_TYPE_RAID5)
>> +{
>> +  grub_dprintf ("btrfs", "RAID5\n");
>> +  nparities = 1;
>> +}
>> +  else
>> +{
>> +  grub_dprintf ("btrfs", "RAID6\n");
>> +  nparities = 2;
>> +}
>> +
>> +  /*
>> +   * Below is an example of a RAID 6 layout and the meaning of the
>> +   * variables. The same applies to RAID 5. The only differences is
>> +   * that there is only one parity disk instead of two.
>> +   *
>> +   * A RAID 6 layout consists of several stripes spread
>> +   * on the disks, following a layout like the one below
>> +   *
>> +   *   Disk1  Disk2  Disk3  Ddisk4
> 
> Numbering seems confusing to me. I think that it should be
>Disk0  Disk1  Disk2  Disk3
> 
>> +   *
>> +   *A1 B1 P1 Q1
>> +   *Q2 A2 B2 P2
>> +   *P3 Q3 A3 B3
>> +   *  [...]
>> +   *
>> +   *  Note that the placement of the parities depends on row index.
>> +   *  In the code below:
>> +   *  - stripe_nr is the stripe number not considering the parities
>> +   *(A1=0, B1=1, A2 = 2, B2 = 3, ...),
> 
> Please be consistent. A1 = 0, B1 = 1, A2 = 2, B2 = 3, ...
> 
>> +   *  - high is the row number (0 for A1...Q1, 1 for Q2..P2, ...),
> 
> Ditto. Please always use "..." not "..".
> 
>> +   *  - stripen is the column number (or disk number),
> 
> AIUI starting from 0. Right? If yes then I think that
> drawing above requires disks/columns renumbering.
right
> 
>> +   *  - off is the logical address to read (from the beginning of
>> +   *the chunk space),
> 
> s/chunk space/chunk/?
> 
>> +   *  - chunk_stripe_length is the size of a stripe (typically 64k),
>> +   *  - nstripes is the number of disks,
>> +   *  - low is the offset of the data inside a stripe,
>> +   *  - stripe_offset is the offset from the beginning of the chunk
>> +   *disks physical address,
> 
> I am not sure that I understand. Could clarify this?
- stripe_offset is the offset (in bytes) from the beginning of the chunk 
portion 
  stored on disk.

You can think "stripe_offset" as the "row" in the drawing, but measured in 
bytes.

> 
>> +   *  - csize is the "potential" data to read. It will be reduced to
>> +   *size if the latter is smaller.
>> +   */
>> +  stripe_nr = grub_divmod64 (off, chunk_stripe_length, );
> 
> OK.
> 
>> +  /*
>> +   * stripen is evaluated without considering
>> +   * the parities (0 for A1, A2, A3... 1 for B1, B2...).
>> +   */
>> +  high = grub_divmod64 (stripe_nr, nstripes - nparities, );
> 
> OK.
> 

Re: Fwd: [PATCH 4/9] btrfs: Avoid a rescan for a device which was already not found.

2018-06-14 Thread Goffredo Baroncelli
On 06/14/2018 01:45 PM, Daniel Kiper wrote:
> On Mon, Jun 04, 2018 at 09:26:06PM +0200, Goffredo Baroncelli wrote:
>> Resend this patch because I am not sure that all received it.
> 
> It looks that #4 is a bit unfortunate for you and/or patch series... :-)))

Yes, but I have another suspect: the patch #4 never changed, and I know that 
gmail "collapse" the emails when these are equal...
> 
>> BR
>> G.Baroncelli
>>
>>
>>  Forwarded Message 
>> Subject: [PATCH 4/9] btrfs: Avoid a rescan for a device which was already 
>> not found.
>> Date: Sun,  3 Jun 2018 20:53:43 +0200
>> From: Goffredo Baroncelli 
>> To: grub-devel@gnu.org
>> CC: Goffredo Baroncelli 
>>
>> If a device is not found, record this failure by storing NULL in
>> data->devices_attached[]. This way we avoid unnecessary devices rescan,
> 
> Hmmm... Could you point me out where this store happens below?

Se below
[...]

>>for (i = 0; i < data->n_devices_attached; i++)
>>  if (id == data->devices_attached[i].id)
>>return data->devices_attached[i].dev;
>> -  if (do_rescan)
>> -grub_device_iterate (find_device_iter, );
>> -  if (!ctx.dev_found)
>> -{
>> -  return NULL;
>> -}

The check above, performs a function exit if ctx.dev_found is NULL. Removing 
this check allows to store the NULL in the array.
In case of another iteration we know that the device is missing without doing a 
rescam

>> +
>> +  grub_device_iterate (find_device_iter, );
>> +
>>data->n_devices_attached++;
>>if (data->n_devices_attached > data->n_devices_allocated)
>>  {
>> @@ -617,7 +614,8 @@ find_device (struct grub_btrfs_data *data, grub_uint64_t 
>> id, int do_rescan)
>>  * sizeof (data->devices_attached[0]));
>>if (!data->devices_attached)
>>  {
>> -  grub_device_close (ctx.dev_found);
>> +  if (ctx.dev_found)
>> +grub_device_close (ctx.dev_found);
>>data->devices_attached = tmp;
>>return NULL;
>>  }
>> @@ -896,7 +894,7 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, 
>> grub_disk_addr_t addr,
>>" for laddr 0x%" PRIxGRUB_UINT64_T "\n", paddr,
>>addr);
>>
>> -dev = find_device (data, stripe->device_id, j);
>> +dev = find_device (data, stripe->device_id);
>>  if (!dev)
>>{
>>  grub_dprintf ("btrfs",
>> @@ -973,7 +971,8 @@ grub_btrfs_unmount (struct grub_btrfs_data *data)
>>unsigned i;
>>/* The device 0 is closed one layer upper.  */
>>for (i = 1; i < data->n_devices_attached; i++)
>> -grub_device_close (data->devices_attached[i].dev);
>> +if (data->devices_attached[i].dev)
>> +grub_device_close (data->devices_attached[i].dev);
>>grub_free (data->devices_attached);
>>grub_free (data->extent);
>>grub_free (data);
>> --
>> 2.17.1
>>
>>
>>
>> ___
>> Grub-devel mailing list
>> Grub-devel@gnu.org
>> https://lists.gnu.org/mailman/listinfo/grub-devel
>>
> 


-- 
gpg @keyserver.linux.it: Goffredo Baroncelli 
Key fingerprint BBF5 1610 0B64 DAC6 5F7D  17B2 0EDA 9B37 8B82 E0B5

___
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel


Re: [PATCH V5] Add support for BTRFS raid5/6 to GRUB

2018-06-14 Thread Goffredo Baroncelli
On 06/14/2018 03:21 PM, Daniel Kiper wrote:
> Hi Goffredo,
> 
> On Sun, Jun 03, 2018 at 08:53:39PM +0200, Goffredo Baroncelli wrote:
>>
>> Hi All,
>>
>> the aim of this patches set is to provide support for a BTRFS raid5/6
>> filesystem in GRUB.
>>
>> The first patch, implements the basic support for raid5/6. I.e this works 
>> when
>> all the disks are present.
>>
>> The next 5 patches, are preparatory ones.
>>
>> The 7th patch implements the raid5 recovery for btrfs (i.e. handling the
>> disappearing of 1 disk).
>> The 8th patch makes the code for handling the raid6 recovery more generic.
>> The last one implements the raid6 recovery for btrfs (i.e. handling the
>> disappearing up to two disks).
>>
>> I tested the code in grub-emu, and it works both with all the disks,
>> and with some disks missing. I checked the crc32 calculated from grub and
>> from linux and these matched. Finally I checked if the support for md raid6
>> still works properly, and it does (with all drives and with up to 2 drives
>> missing)
>>
>> Comments are welcome.
> 
> In general I am happy that you are doing this work. However, I have just
> realized that in some cases you are agreeing with my comments and then
> you do not incorporate the changes which I was asking for. So, I would
> be more happy if you instead of saying OK just do requested changes.

This is not good. I apologize,  If this happened it was a my mistake. 
When I put OK, this means that I agree with your reviews and I incorporate 
them. 
Now I am reviewing your old comments 

> Otherwise you lose your and my time. Hence, I would like ask you to
> check carefully all my comments for v4 and v5 (at least), apply all
> requested changes with which you agree and then post v6.
> 
> Sorry for being blunt.
> 
> Daniel
> 


-- 
gpg @keyserver.linux.it: Goffredo Baroncelli 
Key fingerprint BBF5 1610 0B64 DAC6 5F7D  17B2 0EDA 9B37 8B82 E0B5

___
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel


Fwd: [PATCH 4/9] btrfs: Avoid a rescan for a device which was already not found.

2018-06-04 Thread Goffredo Baroncelli
Resend this patch because I am not sure that all received it.
BR
G.Baroncelli


 Forwarded Message 
Subject: [PATCH 4/9] btrfs: Avoid a rescan for a device which was already not 
found.
Date: Sun,  3 Jun 2018 20:53:43 +0200
From: Goffredo Baroncelli 
To: grub-devel@gnu.org
CC: Goffredo Baroncelli 

If a device is not found, record this failure by storing NULL in
data->devices_attached[]. This way we avoid unnecessary devices rescan,
and speedup the reads in case of a degraded array.

Signed-off-by: Goffredo Baroncelli 
---
 grub-core/fs/btrfs.c | 19 +--
 1 file changed, 9 insertions(+), 10 deletions(-)

diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
index 9b3a22772..b64b692f8 100644
--- a/grub-core/fs/btrfs.c
+++ b/grub-core/fs/btrfs.c
@@ -588,7 +588,7 @@ find_device_iter (const char *name, void *data)
 }
 
 static grub_device_t
-find_device (struct grub_btrfs_data *data, grub_uint64_t id, int do_rescan)
+find_device (struct grub_btrfs_data *data, grub_uint64_t id)
 {
   struct find_device_ctx ctx = {
 .data = data,
@@ -600,12 +600,9 @@ find_device (struct grub_btrfs_data *data, grub_uint64_t 
id, int do_rescan)
   for (i = 0; i < data->n_devices_attached; i++)
 if (id == data->devices_attached[i].id)
   return data->devices_attached[i].dev;
-  if (do_rescan)
-grub_device_iterate (find_device_iter, );
-  if (!ctx.dev_found)
-{
-  return NULL;
-}
+
+  grub_device_iterate (find_device_iter, );
+
   data->n_devices_attached++;
   if (data->n_devices_attached > data->n_devices_allocated)
 {
@@ -617,7 +614,8 @@ find_device (struct grub_btrfs_data *data, grub_uint64_t 
id, int do_rescan)
* sizeof (data->devices_attached[0]));
   if (!data->devices_attached)
{
- grub_device_close (ctx.dev_found);
+ if (ctx.dev_found)
+   grub_device_close (ctx.dev_found);
  data->devices_attached = tmp;
  return NULL;
}
@@ -896,7 +894,7 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, 
grub_disk_addr_t addr,
  " for laddr 0x%" PRIxGRUB_UINT64_T "\n", paddr,
  addr);
 
-   dev = find_device (data, stripe->device_id, j);
+   dev = find_device (data, stripe->device_id);
if (!dev)
  {
grub_dprintf ("btrfs",
@@ -973,7 +971,8 @@ grub_btrfs_unmount (struct grub_btrfs_data *data)
   unsigned i;
   /* The device 0 is closed one layer upper.  */
   for (i = 1; i < data->n_devices_attached; i++)
-grub_device_close (data->devices_attached[i].dev);
+if (data->devices_attached[i].dev)
+grub_device_close (data->devices_attached[i].dev);
   grub_free (data->devices_attached);
   grub_free (data->extent);
   grub_free (data);
-- 
2.17.1



___
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel


[PATCH 8/9] btrfs: Make more generic the code for RAID 6 rebuilding

2018-06-03 Thread Goffredo Baroncelli
The original code which handles the recovery of a RAID 6 disks array
assumes that all reads are multiple of 1 << GRUB_DISK_SECTOR_BITS and it
assumes that all the I/O is done via the struct grub_diskfilter_segment.
This is not true for the btrfs code. In order to reuse the native
grub_raid6_recover() code, it is modified to not call
grub_diskfilter_read_node() directly, but to call an handler passed
as an argument.

Signed-off-by: Goffredo Baroncelli 
---
 grub-core/disk/raid6_recover.c | 52 ++
 include/grub/diskfilter.h  |  9 ++
 2 files changed, 43 insertions(+), 18 deletions(-)

diff --git a/grub-core/disk/raid6_recover.c b/grub-core/disk/raid6_recover.c
index aa674f6ca..0cf691ddf 100644
--- a/grub-core/disk/raid6_recover.c
+++ b/grub-core/disk/raid6_recover.c
@@ -74,14 +74,26 @@ mod_255 (unsigned x)
 }
 
 static grub_err_t
-grub_raid6_recover (struct grub_diskfilter_segment *array, int disknr, int p,
-char *buf, grub_disk_addr_t sector, grub_size_t size)
+raid6_recover_read_node (void *data, int disknr,
+   grub_uint64_t sector,
+   void *buf, grub_size_t size)
+{
+struct grub_diskfilter_segment *array = data;
+
+return grub_diskfilter_read_node (>nodes[disknr],
+ (grub_disk_addr_t)sector,
+ size >> GRUB_DISK_SECTOR_BITS, buf);
+}
+
+grub_err_t
+grub_raid6_recover_gen (void *data, grub_uint64_t nstripes, int disknr, int p,
+   char *buf, grub_uint64_t sector, grub_size_t size,
+   int layout, raid_recover_read_t read_func)
 {
   int i, q, pos;
   int bad1 = -1, bad2 = -1;
   char *pbuf = 0, *qbuf = 0;
 
-  size <<= GRUB_DISK_SECTOR_BITS;
   pbuf = grub_zalloc (size);
   if (!pbuf)
 goto quit;
@@ -91,17 +103,17 @@ grub_raid6_recover (struct grub_diskfilter_segment *array, 
int disknr, int p,
 goto quit;
 
   q = p + 1;
-  if (q == (int) array->node_count)
+  if (q == (int) nstripes)
 q = 0;
 
   pos = q + 1;
-  if (pos == (int) array->node_count)
+  if (pos == (int) nstripes)
 pos = 0;
 
-  for (i = 0; i < (int) array->node_count - 2; i++)
+  for (i = 0; i < (int) nstripes - 2; i++)
 {
   int c;
-  if (array->layout & GRUB_RAID_LAYOUT_MUL_FROM_POS)
+  if (layout & GRUB_RAID_LAYOUT_MUL_FROM_POS)
c = pos;
   else
c = i;
@@ -109,8 +121,7 @@ grub_raid6_recover (struct grub_diskfilter_segment *array, 
int disknr, int p,
 bad1 = c;
   else
 {
-  if (! grub_diskfilter_read_node (>nodes[pos], sector,
-  size >> GRUB_DISK_SECTOR_BITS, buf))
+ if (!read_func(data, pos, sector, buf, size))
 {
   grub_crypto_xor (pbuf, pbuf, buf, size);
   grub_raid_block_mulx (c, buf, size);
@@ -128,7 +139,7 @@ grub_raid6_recover (struct grub_diskfilter_segment *array, 
int disknr, int p,
 }
 
   pos++;
-  if (pos == (int) array->node_count)
+  if (pos == (int) nstripes)
 pos = 0;
 }
 
@@ -139,16 +150,14 @@ grub_raid6_recover (struct grub_diskfilter_segment 
*array, int disknr, int p,
   if (bad2 < 0)
 {
   /* One bad device */
-  if ((! grub_diskfilter_read_node (>nodes[p], sector,
-   size >> GRUB_DISK_SECTOR_BITS, buf)))
+  if (!read_func(data, p, sector, buf, size))
 {
   grub_crypto_xor (buf, buf, pbuf, size);
   goto quit;
 }
 
   grub_errno = GRUB_ERR_NONE;
-  if (grub_diskfilter_read_node (>nodes[q], sector,
-size >> GRUB_DISK_SECTOR_BITS, buf))
+  if (read_func(data, q, sector, buf, size))
 goto quit;
 
   grub_crypto_xor (buf, buf, qbuf, size);
@@ -160,14 +169,12 @@ grub_raid6_recover (struct grub_diskfilter_segment 
*array, int disknr, int p,
   /* Two bad devices */
   unsigned c;
 
-  if (grub_diskfilter_read_node (>nodes[p], sector,
-size >> GRUB_DISK_SECTOR_BITS, buf))
+  if (read_func(data, p, sector, buf, size))
 goto quit;
 
   grub_crypto_xor (pbuf, pbuf, buf, size);
 
-  if (grub_diskfilter_read_node (>nodes[q], sector,
-size >> GRUB_DISK_SECTOR_BITS, buf))
+  if (read_func(data, q, sector, buf, size))
 goto quit;
 
   grub_crypto_xor (qbuf, qbuf, buf, size);
@@ -190,6 +197,15 @@ quit:
   return grub_errno;
 }
 
+static grub_err_t
+grub_raid6_recover (struct grub_diskfilter_segment *array, int disknr, int p,
+char *buf, grub_disk_addr_t sector, grub_size_t size)
+{
+  return grub_raid6_recover_gen (array, array->node_count, disknr, p, buf,
+sector, size 

[PATCH 5/9] btrfs: Move logging code in grub_btrfs_read_logical()

2018-06-03 Thread Goffredo Baroncelli
A portion of the logging code is moved outside of internal for(;;). The part
that is left inside is the one which depends by the internal for(;;) index.
This is a preparatory patch: in the next one it will be possible to refactor
the code inside the for(;;) in an another function.

Signed-off-by: Goffredo Baroncelli 
---
 grub-core/fs/btrfs.c | 25 ++---
 1 file changed, 14 insertions(+), 11 deletions(-)

diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
index b64b692f8..e2baed665 100644
--- a/grub-core/fs/btrfs.c
+++ b/grub-core/fs/btrfs.c
@@ -867,6 +867,18 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, 
grub_disk_addr_t addr,
 
for (j = 0; j < 2; j++)
  {
+   grub_dprintf ("btrfs", "chunk 0x%" PRIxGRUB_UINT64_T
+ "+0x%" PRIxGRUB_UINT64_T
+ " (%d stripes (%d substripes) of %"
+ PRIxGRUB_UINT64_T ") \n",
+ grub_le_to_cpu64 (key->offset),
+ grub_le_to_cpu64 (chunk->size),
+ grub_le_to_cpu16 (chunk->nstripes),
+ grub_le_to_cpu16 (chunk->nsubstripes),
+ grub_le_to_cpu64 (chunk->stripe_length));
+   grub_dprintf ("btrfs", "reading laddr 0x%" PRIxGRUB_UINT64_T "\n",
+ addr);
+
for (i = 0; i < redundancy; i++)
  {
struct grub_btrfs_chunk_stripe *stripe;
@@ -879,20 +891,11 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, 
grub_disk_addr_t addr,
 
paddr = grub_le_to_cpu64 (stripe->offset) + stripe_offset;
 
-   grub_dprintf ("btrfs", "chunk 0x%" PRIxGRUB_UINT64_T
- "+0x%" PRIxGRUB_UINT64_T
- " (%d stripes (%d substripes) of %"
- PRIxGRUB_UINT64_T ") stripe %" PRIxGRUB_UINT64_T
+   grub_dprintf ("btrfs", "stripe %" PRIxGRUB_UINT64_T
  " maps to 0x%" PRIxGRUB_UINT64_T "\n",
- grub_le_to_cpu64 (key->offset),
- grub_le_to_cpu64 (chunk->size),
- grub_le_to_cpu16 (chunk->nstripes),
- grub_le_to_cpu16 (chunk->nsubstripes),
- grub_le_to_cpu64 (chunk->stripe_length),
  stripen, stripe->offset);
grub_dprintf ("btrfs", "reading paddr 0x%" PRIxGRUB_UINT64_T
- " for laddr 0x%" PRIxGRUB_UINT64_T "\n", paddr,
- addr);
+ "\n", paddr);
 
dev = find_device (data, stripe->device_id);
if (!dev)
-- 
2.17.1


___
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel


[PATCH 7/9] btrfs: Add support for recovery for a RAID 5 btrfs profiles.

2018-06-03 Thread Goffredo Baroncelli
Add support for recovery fo a RAID 5 btrfs profile. In addition
it is added some code as preparatory work for RAID 6 recovery code.

Signed-off-by: Goffredo Baroncelli 
---
 grub-core/fs/btrfs.c | 180 +--
 1 file changed, 175 insertions(+), 5 deletions(-)

diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
index 9cdbfe792..c8f034641 100644
--- a/grub-core/fs/btrfs.c
+++ b/grub-core/fs/btrfs.c
@@ -29,6 +29,7 @@
 #include 
 #include 
 #include 
+#include 
 
 GRUB_MOD_LICENSE ("GPLv3+");
 
@@ -666,6 +667,157 @@ btrfs_read_from_chunk (struct grub_btrfs_data *data,
 return err;
 }
 
+struct raid56_buffer {
+  void *buf;
+  int  data_is_valid;
+};
+
+static void
+rebuild_raid5 (char *dest, struct raid56_buffer *buffers,
+  grub_uint64_t nstripes, grub_uint64_t csize)
+{
+  grub_uint64_t i;
+  int first;
+
+  i = 0;
+  while (buffers[i].data_is_valid && i < nstripes)
+++i;
+
+  if (i == nstripes)
+{
+  grub_dprintf ("btrfs", "called rebuild_raid5(), but all disks are OK\n");
+  return;
+}
+
+  grub_dprintf ("btrfs", "rebuilding RAID 5 stripe #%" PRIuGRUB_UINT64_T "\n",
+   i);
+  first = 1;
+  for (i = 0; i < nstripes; i++)
+{
+  if (!buffers[i].data_is_valid)
+   continue;
+
+  if (first)
+   grub_memcpy(dest, buffers[i].buf, csize);
+  else
+   grub_crypto_xor (dest, dest, buffers[i].buf, csize);
+
+  first = 0;
+}
+}
+
+static grub_err_t
+raid56_read_retry (struct grub_btrfs_data *data,
+  struct grub_btrfs_chunk_item *chunk,
+  grub_uint64_t stripe_offset,
+  grub_uint64_t csize, void *buf)
+{
+
+  struct raid56_buffer *buffers = NULL;
+  grub_uint64_t nstripes = grub_le_to_cpu16 (chunk->nstripes);
+  grub_uint64_t chunk_type = grub_le_to_cpu64 (chunk->type);
+  grub_err_t ret = GRUB_ERR_NONE;
+  grub_uint64_t i, failed_devices;
+
+  buffers = grub_zalloc (sizeof(*buffers) * nstripes);
+  if (!buffers)
+{
+  ret = GRUB_ERR_OUT_OF_MEMORY;
+  goto cleanup;
+}
+
+  for (i = 0; i < nstripes; i++)
+{
+  buffers[i].buf = grub_zalloc (csize);
+  if (!buffers[i].buf)
+   {
+ ret = GRUB_ERR_OUT_OF_MEMORY;
+ goto cleanup;
+   }
+}
+
+  for (i = 0; i < nstripes; i++)
+{
+  struct grub_btrfs_chunk_stripe *stripe;
+  grub_disk_addr_t paddr;
+  grub_device_t dev;
+  grub_err_t err2;
+
+  stripe = (struct grub_btrfs_chunk_stripe *) (chunk + 1);
+  stripe += i;
+
+  paddr = grub_le_to_cpu64 (stripe->offset) + stripe_offset;
+  grub_dprintf ("btrfs", "reading paddr %" PRIxGRUB_UINT64_T
+" from stripe ID %" PRIxGRUB_UINT64_T "\n", paddr,
+stripe->device_id);
+
+  dev = find_device (data, stripe->device_id);
+  if (!dev)
+   {
+ buffers[i].data_is_valid = 0;
+ grub_dprintf ("btrfs", "stripe %" PRIuGRUB_UINT64_T " FAILED (dev ID 
%"
+   PRIxGRUB_UINT64_T ")\n", i, stripe->device_id);
+ continue;
+   }
+
+  err2 = grub_disk_read (dev->disk, paddr >> GRUB_DISK_SECTOR_BITS,
+paddr & (GRUB_DISK_SECTOR_SIZE - 1),
+csize, buffers[i].buf);
+  if (err2 == GRUB_ERR_NONE)
+   {
+ buffers[i].data_is_valid = 1;
+ grub_dprintf ("btrfs", "stripe %" PRIuGRUB_UINT64_T " Ok (dev ID %"
+   PRIxGRUB_UINT64_T ")\n", i, stripe->device_id);
+   }
+  else
+   {
+ buffers[i].data_is_valid = 0;
+ grub_dprintf ("btrfs", "stripe %" PRIuGRUB_UINT64_T
+   " FAILED (dev ID %" PRIxGRUB_UINT64_T ")\n", i,
+   stripe->device_id);
+   }
+}
+
+  failed_devices = 0;
+  for (i = 0; i < nstripes; i++)
+if (!buffers[i].data_is_valid)
+  ++failed_devices;
+  if (failed_devices > 1 && (chunk_type & GRUB_BTRFS_CHUNK_TYPE_RAID5))
+{
+  grub_dprintf ("btrfs",
+   "not enough disks for RAID 5: total %" PRIuGRUB_UINT64_T
+   ", missing %" PRIuGRUB_UINT64_T "\n",
+   nstripes, failed_devices);
+  ret = GRUB_ERR_READ_ERROR;
+  goto cleanup;
+}
+  else
+{
+  grub_dprintf ("btrfs",
+"enough disks for RAID 5 rebuilding: total %"
+   PRIuGRUB_UINT64_T ", missing %" PRIuGRUB_UINT64_T "\n",
+nstripes, failed_devices);
+}
+
+  /* if these are enough, try to rebuild the data */
+  if (chunk_type & GRUB_BTRFS_CHUNK_TYPE_RAID5)
+ 

[PATCH 9/9] btrfs: Add RAID 6 recovery for a btrfs filesystem.

2018-06-03 Thread Goffredo Baroncelli
Add the RAID 6 recovery, in order to use a RAID 6 filesystem even if some
disks (up to two) are missing. This code use the md RAID 6 code already
present in grub.

Signed-off-by: Goffredo Baroncelli 
---
 grub-core/fs/btrfs.c | 50 ++--
 1 file changed, 44 insertions(+), 6 deletions(-)

diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
index c8f034641..0b6557ce3 100644
--- a/grub-core/fs/btrfs.c
+++ b/grub-core/fs/btrfs.c
@@ -30,6 +30,7 @@
 #include 
 #include 
 #include 
+#include 
 
 GRUB_MOD_LICENSE ("GPLv3+");
 
@@ -706,11 +707,35 @@ rebuild_raid5 (char *dest, struct raid56_buffer *buffers,
 }
 }
 
+static grub_err_t
+raid6_recover_read_buffer (void *data, int disk_nr,
+  grub_uint64_t addr __attribute__ ((unused)),
+  void *dest, grub_size_t size)
+{
+struct raid56_buffer *buffers = data;
+
+grub_memcpy(dest, buffers[disk_nr].buf, size);
+
+grub_errno = buffers[disk_nr].data_is_valid ? GRUB_ERR_NONE :
+GRUB_ERR_READ_ERROR;
+return grub_errno;
+}
+
+static void
+rebuild_raid6 (struct raid56_buffer *buffers, grub_uint64_t nstripes,
+   grub_uint64_t csize, grub_uint64_t parities_pos, void *dest,
+   grub_uint64_t stripen)
+
+{
+  grub_raid6_recover_gen (buffers, nstripes, stripen, parities_pos,
+  dest, 0, csize, 0, raid6_recover_read_buffer);
+}
+
 static grub_err_t
 raid56_read_retry (struct grub_btrfs_data *data,
   struct grub_btrfs_chunk_item *chunk,
-  grub_uint64_t stripe_offset,
-  grub_uint64_t csize, void *buf)
+  grub_uint64_t stripe_offset, grub_uint64_t stripen,
+  grub_uint64_t csize, void *buf, grub_uint64_t parities_pos)
 {
 
   struct raid56_buffer *buffers = NULL;
@@ -791,6 +816,15 @@ raid56_read_retry (struct grub_btrfs_data *data,
   ret = GRUB_ERR_READ_ERROR;
   goto cleanup;
 }
+  else if (failed_devices > 2 && (chunk_type & GRUB_BTRFS_CHUNK_TYPE_RAID6))
+{
+  grub_dprintf ("btrfs",
+   "not enough disks for raid6: total %" PRIuGRUB_UINT64_T
+   ", missing %" PRIuGRUB_UINT64_T "\n",
+   nstripes, failed_devices);
+  ret = GRUB_ERR_READ_ERROR;
+  goto cleanup;
+}
   else
 {
   grub_dprintf ("btrfs",
@@ -803,7 +837,7 @@ raid56_read_retry (struct grub_btrfs_data *data,
   if (chunk_type & GRUB_BTRFS_CHUNK_TYPE_RAID5)
 rebuild_raid5 (buf, buffers, nstripes, csize);
   else
-grub_dprintf ("btrfs", "called rebuild_raid6(), NOT IMPLEMENTED\n");
+rebuild_raid6 (buffers, nstripes, csize, parities_pos, buf, stripen);
 
 cleanup:
 
@@ -896,9 +930,11 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, 
grub_disk_addr_t addr,
unsigned redundancy = 1;
unsigned i, j;
int is_raid56;
+   grub_uint64_t parities_pos = 0;
 
-   is_raid56 = !!(grub_le_to_cpu64 (chunk->type) &
-  GRUB_BTRFS_CHUNK_TYPE_RAID5);
+is_raid56 = !!(grub_le_to_cpu64 (chunk->type) &
+  (GRUB_BTRFS_CHUNK_TYPE_RAID5 |
+   GRUB_BTRFS_CHUNK_TYPE_RAID6));
 
if (grub_le_to_cpu64 (chunk->size) <= off)
  {
@@ -1043,6 +1079,8 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, 
grub_disk_addr_t addr,
   * 2 for A3). The math is performed modulo number of disks.
   */
  grub_divmod64 (high + stripen, nstripes, );
+ grub_divmod64 (high + nstripes - nparities, nstripes,
+_pos);
 
  stripe_offset = low + chunk_stripe_length * high;
  csize = chunk_stripe_length - low;
@@ -1097,7 +1135,7 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, 
grub_disk_addr_t addr,
grub_errno = GRUB_ERR_NONE;
if (err != GRUB_ERR_NONE)
  err = raid56_read_retry (data, chunk, stripe_offset,
-  csize, buf);
+  stripen, csize, buf, parities_pos);
  }
if (err == GRUB_ERR_NONE)
  break;
-- 
2.17.1


___
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel


[PATCH V5] Add support for BTRFS raid5/6 to GRUB

2018-06-03 Thread Goffredo Baroncelli


Hi All,

the aim of this patches set is to provide support for a BTRFS raid5/6
filesystem in GRUB.

The first patch, implements the basic support for raid5/6. I.e this works when
all the disks are present.

The next 5 patches, are preparatory ones.

The 7th patch implements the raid5 recovery for btrfs (i.e. handling the
disappearing of 1 disk).
The 8th patch makes the code for handling the raid6 recovery more generic.
The last one implements the raid6 recovery for btrfs (i.e. handling the
disappearing up to two disks).

I tested the code in grub-emu, and it works both with all the disks,
and with some disks missing. I checked the crc32 calculated from grub and
from linux and these matched. Finally I checked if the support for md raid6
still works properly, and it does (with all drives and with up to 2 drives
missing)

Comments are welcome.

Changelog
v1: initial support for btrfs raid5/6. No recovery allowed
v2: full support for btrfs raid5/6. Recovery allowed
v3: some minor cleanup suggested by Daniel Kiper; reusing the
original raid6 recovery code of grub
v4: Several spell fix; better description of the RAID layout
in btrfs, and the variables which describes the stripe
positioning; split the patch #5 in two (#5 and #6)
v5: Several spell fix; improved code comment in patch #1, small
clean up in the code


BR
G.Baroncelli
--
gpg @keyserver.linux.it: Goffredo Baroncelli 
Key fingerprint BBF5 1610 0B64 DAC6 5F7D  17B2 0EDA 9B37 8B82 E0B5


___
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel


[PATCH 6/9] btrfs: Refactor the code that read from disk

2018-06-03 Thread Goffredo Baroncelli
Move the code in charge to read the data from disk in a separate
function. This helps to separate the error handling logic (which depend by
the different raid profiles) from the read from disk logic.
Refactoring this code increases the general readability too.

This is a preparatory patch, to help the adding of the RAID 5/6 recovery
code.

Signed-off-by: Goffredo Baroncelli 
---
 grub-core/fs/btrfs.c | 76 ++--
 1 file changed, 45 insertions(+), 31 deletions(-)

diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
index e2baed665..9cdbfe792 100644
--- a/grub-core/fs/btrfs.c
+++ b/grub-core/fs/btrfs.c
@@ -625,6 +625,47 @@ find_device (struct grub_btrfs_data *data, grub_uint64_t 
id)
   return ctx.dev_found;
 }
 
+static grub_err_t
+btrfs_read_from_chunk (struct grub_btrfs_data *data,
+  struct grub_btrfs_chunk_item *chunk,
+  grub_uint64_t stripen, grub_uint64_t stripe_offset,
+  int redundancy, grub_uint64_t csize,
+  void *buf)
+{
+
+struct grub_btrfs_chunk_stripe *stripe;
+grub_disk_addr_t paddr;
+grub_device_t dev;
+grub_err_t err;
+
+stripe = (struct grub_btrfs_chunk_stripe *) (chunk + 1);
+/* Right now the redundancy handling is easy.
+   With RAID5-like it will be more difficult.  */
+stripe += stripen + redundancy;
+
+paddr = grub_le_to_cpu64 (stripe->offset) + stripe_offset;
+
+grub_dprintf ("btrfs", "stripe %" PRIxGRUB_UINT64_T
+ " maps to 0x%" PRIxGRUB_UINT64_T "\n",
+ stripen, stripe->offset);
+grub_dprintf ("btrfs", "reading paddr 0x%" PRIxGRUB_UINT64_T "\n", paddr);
+
+dev = find_device (data, stripe->device_id);
+if (!dev)
+  {
+   grub_dprintf ("btrfs",
+ "couldn't find a necessary member device "
+ "of multi-device filesystem\n");
+   grub_errno = GRUB_ERR_NONE;
+   return GRUB_ERR_READ_ERROR;
+  }
+
+err = grub_disk_read (dev->disk, paddr >> GRUB_DISK_SECTOR_BITS,
+ paddr & (GRUB_DISK_SECTOR_SIZE - 1),
+ csize, buf);
+return err;
+}
+
 static grub_err_t
 grub_btrfs_read_logical (struct grub_btrfs_data *data, grub_disk_addr_t addr,
 void *buf, grub_size_t size, int recursion_depth)
@@ -638,7 +679,6 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, 
grub_disk_addr_t addr,
   grub_err_t err = 0;
   struct grub_btrfs_key key_out;
   int challoc = 0;
-  grub_device_t dev;
   struct grub_btrfs_key key_in;
   grub_size_t chsize;
   grub_disk_addr_t chaddr;
@@ -881,36 +921,10 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, 
grub_disk_addr_t addr,
 
for (i = 0; i < redundancy; i++)
  {
-   struct grub_btrfs_chunk_stripe *stripe;
-   grub_disk_addr_t paddr;
-
-   stripe = (struct grub_btrfs_chunk_stripe *) (chunk + 1);
-   /* Right now the redundancy handling is easy.
-  With RAID5-like it will be more difficult.  */
-   stripe += stripen + i;
-
-   paddr = grub_le_to_cpu64 (stripe->offset) + stripe_offset;
-
-   grub_dprintf ("btrfs", "stripe %" PRIxGRUB_UINT64_T
- " maps to 0x%" PRIxGRUB_UINT64_T "\n",
- stripen, stripe->offset);
-   grub_dprintf ("btrfs", "reading paddr 0x%" PRIxGRUB_UINT64_T
- "\n", paddr);
-
-   dev = find_device (data, stripe->device_id);
-   if (!dev)
- {
-   grub_dprintf ("btrfs",
- "couldn't find a necessary member device "
- "of multi-device filesystem\n");
-   err = grub_errno;
-   grub_errno = GRUB_ERR_NONE;
-   continue;
- }
-
-   err = grub_disk_read (dev->disk, paddr >> GRUB_DISK_SECTOR_BITS,
- paddr & (GRUB_DISK_SECTOR_SIZE - 1),
- csize, buf);
+   err = btrfs_read_from_chunk (data, chunk, stripen,
+stripe_offset,
+i, /* redundancy */
+csize, buf);
if (!err)
  break;
grub_errno = GRUB_ERR_NONE;
-- 
2.17.1


___
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel


[PATCH 3/9] btrfs: Move the error logging from find_device() to its caller.

2018-06-03 Thread Goffredo Baroncelli
This is a preparatory patch. The caller knows better if this
error is fatal or not, i.e. another disk is available or not.


Signed-off-by: Goffredo Baroncelli 
---
 grub-core/fs/btrfs.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
index 669c49301..9b3a22772 100644
--- a/grub-core/fs/btrfs.c
+++ b/grub-core/fs/btrfs.c
@@ -604,9 +604,6 @@ find_device (struct grub_btrfs_data *data, grub_uint64_t 
id, int do_rescan)
 grub_device_iterate (find_device_iter, );
   if (!ctx.dev_found)
 {
-  grub_error (GRUB_ERR_BAD_FS,
- N_("couldn't find a necessary member device "
-"of multi-device filesystem"));
   return NULL;
 }
   data->n_devices_attached++;
@@ -902,6 +899,9 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, 
grub_disk_addr_t addr,
dev = find_device (data, stripe->device_id, j);
if (!dev)
  {
+   grub_dprintf ("btrfs",
+ "couldn't find a necessary member device "
+ "of multi-device filesystem\n");
err = grub_errno;
grub_errno = GRUB_ERR_NONE;
continue;
-- 
2.17.1


___
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel


[PATCH 1/9] btrfs: Add support for reading a filesystem with a RAID 5 or RAID 6 profile.

2018-06-03 Thread Goffredo Baroncelli
Signed-off-by: Goffredo Baroncelli 
---
 grub-core/fs/btrfs.c | 70 
 1 file changed, 70 insertions(+)

diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
index be195448d..4d418859b 100644
--- a/grub-core/fs/btrfs.c
+++ b/grub-core/fs/btrfs.c
@@ -119,6 +119,8 @@ struct grub_btrfs_chunk_item
 #define GRUB_BTRFS_CHUNK_TYPE_RAID1 0x10
 #define GRUB_BTRFS_CHUNK_TYPE_DUPLICATED0x20
 #define GRUB_BTRFS_CHUNK_TYPE_RAID100x40
+#define GRUB_BTRFS_CHUNK_TYPE_RAID5 0x80
+#define GRUB_BTRFS_CHUNK_TYPE_RAID6 0x100
   grub_uint8_t dummy2[0xc];
   grub_uint16_t nstripes;
   grub_uint16_t nsubstripes;
@@ -764,6 +766,74 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, 
grub_disk_addr_t addr,
  stripe_offset = low + chunk_stripe_length
* high;
  csize = chunk_stripe_length - low;
+ break;
+   }
+ case GRUB_BTRFS_CHUNK_TYPE_RAID5:
+ case GRUB_BTRFS_CHUNK_TYPE_RAID6:
+   {
+ grub_uint64_t nparities, stripe_nr, high, low;
+
+ redundancy = 1;   /* no redundancy for now */
+
+ if (grub_le_to_cpu64 (chunk->type) & GRUB_BTRFS_CHUNK_TYPE_RAID5)
+   {
+ grub_dprintf ("btrfs", "RAID5\n");
+ nparities = 1;
+   }
+ else
+   {
+ grub_dprintf ("btrfs", "RAID6\n");
+ nparities = 2;
+   }
+
+ /*
+  * Below is an example of a RAID 6 layout and the meaning of the
+  * variables. The same applies to RAID 5. The only differences is
+  * that there is only one parity disk instead of two.
+  *
+  * A RAID 6 layout consists of several stripes spread
+  * on the disks, following a layout like the one below
+  *
+  *   Disk1  Disk2  Disk3  Ddisk4
+  *
+  *A1 B1 P1 Q1
+  *Q2 A2 B2 P2  
+  *P3 Q3 A3 B3
+  *  [...]
+  *
+  *  Note that the placement of the parities depends on row index.
+  *  In the code below:
+  *  - stripe_nr is the stripe number not considering the parities
+  *(A1=0, B1=1, A2 = 2, B2 = 3, ...),
+  *  - high is the row number (0 for A1...Q1, 1 for Q2..P2, ...),
+  *  - stripen is the column number (or disk number),
+  *  - off is the logical address to read (from the beginning of
+  *the chunk space),
+  *  - chunk_stripe_length is the size of a stripe (typically 64k),
+  *  - nstripes is the number of disks,
+  *  - low is the offset of the data inside a stripe,
+  *  - stripe_offset is the offset from the beginning of the chunk
+  *disks physical address,
+  *  - csize is the "potential" data to read. It will be reduced to
+  *size if the latter is smaller.
+  */
+ stripe_nr = grub_divmod64 (off, chunk_stripe_length, );
+
+ /*
+  * stripen is evaluated without considering
+  * the parities (0 for A1, A2, A3... 1 for B1, B2...).
+  */
+ high = grub_divmod64 (stripe_nr, nstripes - nparities, );
+
+ /*
+  * stripen now considers also the parities (0 for A1, 1 for A2,
+  * 2 for A3). The math is performed modulo number of disks.
+  */
+ grub_divmod64 (high + stripen, nstripes, );
+
+ stripe_offset = low + chunk_stripe_length * high;
+ csize = chunk_stripe_length - low;
+
  break;
}
  default:
-- 
2.17.1


___
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel


[PATCH 2/9] btrfs: Add helper to check the btrfs header.

2018-06-03 Thread Goffredo Baroncelli
This helper will be used in a few places to help the debugging. As
conservative approach, in case of error it is only logged. This is
because I am not sure if this can change something in the error
handling of the currently existing code.

Signed-off-by: Goffredo Baroncelli 
---
 grub-core/fs/btrfs.c | 24 +++-
 1 file changed, 23 insertions(+), 1 deletion(-)

diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
index 4d418859b..669c49301 100644
--- a/grub-core/fs/btrfs.c
+++ b/grub-core/fs/btrfs.c
@@ -77,7 +77,8 @@ struct btrfs_header
 {
   grub_btrfs_checksum_t checksum;
   grub_btrfs_uuid_t uuid;
-  grub_uint8_t dummy[0x30];
+  grub_uint64_t bytenr;
+  grub_uint8_t dummy[0x28];
   grub_uint32_t nitems;
   grub_uint8_t level;
 } GRUB_PACKED;
@@ -286,6 +287,25 @@ free_iterator (struct grub_btrfs_leaf_descriptor *desc)
   grub_free (desc->data);
 }
 
+static grub_err_t
+check_btrfs_header (struct grub_btrfs_data *data, struct btrfs_header *header,
+grub_disk_addr_t addr)
+{
+  if (grub_le_to_cpu64 (header->bytenr) != addr)
+{
+  grub_dprintf ("btrfs", "btrfs_header.bytenr is not equal node addr\n");
+  return grub_error (GRUB_ERR_BAD_FS,
+"header bytenr is not equal node addr");
+}
+  if (grub_memcmp (data->sblock.uuid, header->uuid, sizeof(grub_btrfs_uuid_t)))
+{
+  grub_dprintf ("btrfs", "btrfs_header.uuid doesn't match sblock uuid\n");
+  return grub_error (GRUB_ERR_BAD_FS,
+"header uuid doesn't match sblock uuid");
+}
+  return GRUB_ERR_NONE;
+}
+
 static grub_err_t
 save_ref (struct grub_btrfs_leaf_descriptor *desc,
  grub_disk_addr_t addr, unsigned i, unsigned m, int l)
@@ -341,6 +361,7 @@ next (struct grub_btrfs_data *data,
 
   err = grub_btrfs_read_logical (data, grub_le_to_cpu64 (node.addr),
 , sizeof (head), 0);
+  check_btrfs_header (data, , grub_le_to_cpu64 (node.addr));
   if (err)
return -err;
 
@@ -402,6 +423,7 @@ lower_bound (struct grub_btrfs_data *data,
   /* FIXME: preread few nodes into buffer. */
   err = grub_btrfs_read_logical (data, addr, , sizeof (head),
 recursion_depth + 1);
+  check_btrfs_header (data, , addr);
   if (err)
return err;
   addr += sizeof (head);
-- 
2.17.1


___
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel


Re: [PATCH 6/9] btrfs: refactor the code that read from disk

2018-06-01 Thread Goffredo Baroncelli
On 05/30/2018 01:07 PM, Daniel Kiper wrote:
> On Wed, May 16, 2018 at 08:48:16PM +0200, Goffredo Baroncelli wrote:
>> This is a preparatory patch, to help the adding of the RAID 5/6 recovery
> 
> Please move "This is a preparatory patch" sentence below...
> 
>> code. In case of availability of all disks, this code is good for all the
>> RAID profiles. However in case of failure, the error handling is quite
>> different. Refactoring this code increases the general readability.
> 
> s/readability/readability too/?
> 
> You can put "This is a preparatory patch" in separate line here.
> Same for the other patches.

What about

btrfs: Refactor the code that read from disk

Move the code in charge to read the data from disk in a separate
function. This helps to separate the error handling logic (which depend by 
the different raid profiles) from the read from disk logic.
Refactoring this code increases the general readability too.

This is a preparatory patch, to help the adding of the RAID 5/6 recovery
code. 




> 
>> Signed-off-by: Goffredo Baroncelli 
>> ---
>>  grub-core/fs/btrfs.c | 85 +---
>>  1 file changed, 49 insertions(+), 36 deletions(-)
>>
>> diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
>> index 51f300829..63651928b 100644
>> --- a/grub-core/fs/btrfs.c
>> +++ b/grub-core/fs/btrfs.c
>> @@ -625,6 +625,47 @@ find_device (struct grub_btrfs_data *data, 
>> grub_uint64_t id)
>>return ctx.dev_found;
>>  }
>>
>> +static grub_err_t
>> +btrfs_read_from_chunk (struct grub_btrfs_data *data,
>> +   struct grub_btrfs_chunk_item *chunk,
>> +   grub_uint64_t stripen, grub_uint64_t stripe_offset,
>> +   int redundancy, grub_uint64_t csize,
>> +   void *buf)
>> +{
>> +
>> +struct grub_btrfs_chunk_stripe *stripe;
>> +grub_disk_addr_t paddr;
>> +grub_device_t dev;
>> +grub_err_t err;
>> +
>> +stripe = (struct grub_btrfs_chunk_stripe *) (chunk + 1);
>> +/* Right now the redundancy handling is easy.
>> +   With RAID5-like it will be more difficult.  */
>> +stripe += stripen + redundancy;
>> +
>> +paddr = grub_le_to_cpu64 (stripe->offset) + stripe_offset;
>> +
>> +grub_dprintf ("btrfs", "stripe %" PRIxGRUB_UINT64_T
>> +  " maps to 0x%" PRIxGRUB_UINT64_T "\n",
>> +  stripen, stripe->offset);
>> +grub_dprintf ("btrfs", "reading paddr 0x%" PRIxGRUB_UINT64_T "\n", 
>> paddr);
>> +
>> +dev = find_device (data, stripe->device_id);
>> +if (!dev)
>> +  {
>> +grub_dprintf ("btrfs",
>> +  "couldn't find a necessary member device "
>> +  "of multi-device filesystem\n");
>> +grub_errno = GRUB_ERR_NONE;
>> +return GRUB_ERR_READ_ERROR;
>> +  }
>> +
>> +err = grub_disk_read (dev->disk, paddr >> GRUB_DISK_SECTOR_BITS,
>> +  paddr & (GRUB_DISK_SECTOR_SIZE - 1),
>> +  csize, buf);
>> +return err;
>> +}
>> +
>>  static grub_err_t
>>  grub_btrfs_read_logical (struct grub_btrfs_data *data, grub_disk_addr_t 
>> addr,
>>   void *buf, grub_size_t size, int recursion_depth)
>> @@ -638,7 +679,6 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, 
>> grub_disk_addr_t addr,
>>grub_err_t err = 0;
>>struct grub_btrfs_key key_out;
>>int challoc = 0;
>> -  grub_device_t dev;
>>struct grub_btrfs_key key_in;
>>grub_size_t chsize;
>>grub_disk_addr_t chaddr;
>> @@ -879,42 +919,15 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, 
>> grub_disk_addr_t addr,
>>
>>  for (i = 0; i < redundancy; i++)
>>{
>> -struct grub_btrfs_chunk_stripe *stripe;
>> -grub_disk_addr_t paddr;
>> -
>> -stripe = (struct grub_btrfs_chunk_stripe *) (chunk + 1);
>> -/* Right now the redundancy handling is easy.
>> -   With RAID5-like it will be more difficult.  */
>> -stripe += stripen + i;
>> -
>> -paddr = grub_le_to_cpu64 (stripe->offset) + stripe_offset;
>> -
>> -grub_dprintf ("btrfs", "stripe %" PRIxGRUB_UINT64_T
>> -   

Re: [PATCH 8/9] btrfs: make more generic the code for RAID 6 rebuilding

2018-06-01 Thread Goffredo Baroncelli
On 05/30/2018 02:05 PM, Daniel Kiper wrote:
> On Wed, May 16, 2018 at 08:48:18PM +0200, Goffredo Baroncelli wrote:
>> The original code which handles the recovery of a RAID 6 disks array
>> assumes that all reads are multiple of 1 << GRUB_DISK_SECTOR_BITS and it
>> assumes that all the I/O is done via the struct grub_diskfilter_segment.
>> This is not true for the btrfs code. In order to reuse the native
>> grub_raid6_recover() code, it is modified to not call
>> grub_diskfilter_read_node() directly, but to call an handler passed
>> as argument.
> 
> s/as argument/as an argument/
ok
> 
>> Signed-off-by: Goffredo Baroncelli 
>> ---
>>  grub-core/disk/raid6_recover.c | 53 ++
>>  include/grub/diskfilter.h  |  9 ++
>>  2 files changed, 44 insertions(+), 18 deletions(-)
>>
>> diff --git a/grub-core/disk/raid6_recover.c b/grub-core/disk/raid6_recover.c
>> index aa674f6ca..10ee495e2 100644
>> --- a/grub-core/disk/raid6_recover.c
>> +++ b/grub-core/disk/raid6_recover.c
>> @@ -74,14 +74,25 @@ mod_255 (unsigned x)
>>  }
>>
>>  static grub_err_t
>> -grub_raid6_recover (struct grub_diskfilter_segment *array, int disknr, int 
>> p,
>> -char *buf, grub_disk_addr_t sector, grub_size_t size)
>> +raid6_recover_read_node (void *data, int disk_nr,
> 
> s/disk_nr/disknr/

OK

>> +grub_uint64_t sector,
>> +void *buf, grub_size_t size)
>> +{
>> +struct grub_diskfilter_segment *array = data;
> 
> Please add one empty line here.
> 
>> +return grub_diskfilter_read_node (>nodes[disk_nr],
>> +  (grub_disk_addr_t)sector,
>> +  size >> GRUB_DISK_SECTOR_BITS, buf);
>> +}
>> +
>> +grub_err_t
>> +grub_raid6_recover_gen (void *data, grub_uint64_t nstripes, int disknr, int 
>> p,
>> +char *buf, grub_uint64_t sector, grub_size_t size,
>> +raid_recover_read_t read_func, int layout)
> 
> Could you change the order of read_func and layout arguments?
> I mean make read_func the last one and put the layout before it.
OK
> 
>>  {
>>int i, q, pos;
>>int bad1 = -1, bad2 = -1;
>>char *pbuf = 0, *qbuf = 0;
>>
>> -  size <<= GRUB_DISK_SECTOR_BITS;
>>pbuf = grub_zalloc (size);
>>if (!pbuf)
>>  goto quit;
>> @@ -91,17 +102,17 @@ grub_raid6_recover (struct grub_diskfilter_segment 
>> *array, int disknr, int p,
>>  goto quit;
>>
>>q = p + 1;
>> -  if (q == (int) array->node_count)
>> +  if (q == (int) nstripes)
>>  q = 0;
>>
>>pos = q + 1;
>> -  if (pos == (int) array->node_count)
>> +  if (pos == (int) nstripes)
>>  pos = 0;
>>
>> -  for (i = 0; i < (int) array->node_count - 2; i++)
>> +  for (i = 0; i < (int) nstripes - 2; i++)
>>  {
>>int c;
>> -  if (array->layout & GRUB_RAID_LAYOUT_MUL_FROM_POS)
>> +  if (layout & GRUB_RAID_LAYOUT_MUL_FROM_POS)
>>  c = pos;
>>else
>>  c = i;
>> @@ -109,8 +120,7 @@ grub_raid6_recover (struct grub_diskfilter_segment 
>> *array, int disknr, int p,
>>  bad1 = c;
>>else
>>  {
>> -  if (! grub_diskfilter_read_node (>nodes[pos], sector,
>> -   size >> GRUB_DISK_SECTOR_BITS, buf))
>> +  if (!read_func(data, pos, sector, buf, size))
>>  {
>>grub_crypto_xor (pbuf, pbuf, buf, size);
>>grub_raid_block_mulx (c, buf, size);
>> @@ -128,7 +138,7 @@ grub_raid6_recover (struct grub_diskfilter_segment 
>> *array, int disknr, int p,
>>  }
>>
>>pos++;
>> -  if (pos == (int) array->node_count)
>> +  if (pos == (int) nstripes)
>>  pos = 0;
>>  }
>>
>> @@ -139,16 +149,14 @@ grub_raid6_recover (struct grub_diskfilter_segment 
>> *array, int disknr, int p,
>>if (bad2 < 0)
>>  {
>>/* One bad device */
>> -  if ((! grub_diskfilter_read_node (>nodes[p], sector,
>> -size >> GRUB_DISK_SECTOR_BITS, buf)))
>> +  if (!read_func(data, p, sector, buf, size))
>>  {
>>grub_crypto_xor (buf, buf, pbuf, size);
>>goto quit;
>>  }
>>
>>grub_errno = GR

Re: [PATCH 9/9] btrfs: add RAID 6 recovery for a btrfs filesystem.

2018-06-01 Thread Goffredo Baroncelli
On 05/30/2018 02:15 PM, Daniel Kiper wrote:
> On Wed, May 16, 2018 at 08:48:19PM +0200, Goffredo Baroncelli wrote:
>> Add the RAID 6 recovery, in order to use a RAID 6 filesystem even if some
>> disks (up to two) are missing. This code use the old md RAID 6 code already
> 
> s/the old md/the md/

ok

>> present in grub.
>>
>> Signed-off-by: Goffredo Baroncelli 
>> ---
>>  grub-core/fs/btrfs.c | 45 +++-
>>  1 file changed, 40 insertions(+), 5 deletions(-)
>>
>> diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
>> index 5fcaad86f..3d71b954e 100644
>> --- a/grub-core/fs/btrfs.c
>> +++ b/grub-core/fs/btrfs.c
>> @@ -30,6 +30,7 @@
>>  #include 
>>  #include 
>>  #include 
>> +#include 
>>
>>  GRUB_MOD_LICENSE ("GPLv3+");
>>
>> @@ -695,11 +696,35 @@ rebuild_raid5 (struct raid56_buffer *buffers, 
>> grub_uint64_t nstripes,
>> csize);
>>  }
>>
>> +static grub_err_t
>> +raid6_recover_read_buffer (void *data, int disk_nr,
>> +   grub_uint64_t addr __attribute__ ((unused)),
>> +   void *dest, grub_size_t size)
>> +{
>> +struct raid56_buffer *buffers = data;
>> +
>> +grub_memcpy(dest, buffers[disk_nr].buf, size);
> 
> Could we avoid this grub_memcpy() call somehow?

I would like; however this would mean that the handling of the raid 5 and raid 
6 rebuilding code would be totally different. This has a big impact on the 
patches set and the related tests performed until now.
Are you sure that this effort will be really needed ?
> 
>> +grub_errno = buffers[disk_nr].data_is_valid ? GRUB_ERR_NONE :
>> + GRUB_ERR_READ_ERROR;
>> +return grub_errno;
>> +}
>> +
>> +static void
>> +rebuild_raid6 (struct raid56_buffer *buffers, grub_uint64_t nstripes,
>> +   grub_uint64_t csize, grub_uint64_t parities_pos, void *dest,
>> +   grub_uint64_t stripen)
>> +
>> +{
>> +  grub_raid6_recover_gen (buffers, nstripes, stripen, parities_pos,
>> +  dest, 0, csize, raid6_recover_read_buffer, 0);
>> +}
>> +
>>  static grub_err_t
>>  raid56_read_retry (struct grub_btrfs_data *data,
>> struct grub_btrfs_chunk_item *chunk,
>> grub_uint64_t stripe_offset, grub_uint64_t stripen,
>> -   grub_uint64_t csize, void *buf)
>> +   grub_uint64_t csize, void *buf, grub_uint64_t parities_pos)
>>  {
>>
>>struct raid56_buffer *buffers = NULL;
>> @@ -780,6 +805,15 @@ raid56_read_retry (struct grub_btrfs_data *data,
>>ret = GRUB_ERR_READ_ERROR;
>>goto cleanup;
>>  }
>> +  else if (failed_devices > 2 && (chunk_type & GRUB_BTRFS_CHUNK_TYPE_RAID6))
>> +{
>> +  grub_dprintf ("btrfs",
>> +"not enough disks for raid6: total %" PRIuGRUB_UINT64_T
>> +", missing %" PRIuGRUB_UINT64_T "\n",
>> +nstripes, failed_devices);
>> +  ret = GRUB_ERR_READ_ERROR;
>> +  goto cleanup;
>> +}
>>else
>>  {
>>grub_dprintf ("btrfs",
>> @@ -796,7 +830,7 @@ raid56_read_retry (struct grub_btrfs_data *data,
>>  }
>>else
>>  {
>> -  grub_dprintf ("btrfs", "called rebuild_raid6(), NOT IMPLEMENTED\n");
>> +  rebuild_raid6 (buffers, nstripes, csize, parities_pos, buf, stripen);
>>  }
>>
>>  cleanup:
>> @@ -891,8 +925,9 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, 
>> grub_disk_addr_t addr,
>>  int is_raid56;
>>  grub_uint64_t parities_pos = 0;
>>
>> -is_raid56 = !!(grub_le_to_cpu64 (chunk->type) &
>> -   GRUB_BTRFS_CHUNK_TYPE_RAID5);
>> +is_raid56 = !!(grub_le_to_cpu64 (chunk->type) &
>> +   (GRUB_BTRFS_CHUNK_TYPE_RAID5|
> 
> Space before "|" please?
OK
> 
>> +GRUB_BTRFS_CHUNK_TYPE_RAID6));
>>
>>  if (grub_le_to_cpu64 (chunk->size) <= off)
>>{
>> @@ -1089,7 +1124,7 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, 
>> grub_disk_addr_t addr,
>>   csize, buf);
>>  if (err != GRUB_ERR_NONE)
>>err = raid56_read_retry (data, chunk, stripe_offset,
>> -   stripen, csize,

Re: [PATCH 7/9] btrfs: add support for recovery for a RAID 5 btrfs profiles.

2018-06-01 Thread Goffredo Baroncelli
On 05/30/2018 01:30 PM, Daniel Kiper wrote:
> On Wed, May 16, 2018 at 08:48:17PM +0200, Goffredo Baroncelli wrote:
>> Signed-off-by: Goffredo Baroncelli 
>> ---
>>  grub-core/fs/btrfs.c | 174 ++-
>>  1 file changed, 170 insertions(+), 4 deletions(-)
>>
>> diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
>> index 63651928b..5fcaad86f 100644
>> --- a/grub-core/fs/btrfs.c
>> +++ b/grub-core/fs/btrfs.c
>> @@ -29,6 +29,7 @@
>>  #include 
>>  #include 
>>  #include 
>> +#include 
>>
>>  GRUB_MOD_LICENSE ("GPLv3+");
>>
>> @@ -666,6 +667,150 @@ btrfs_read_from_chunk (struct grub_btrfs_data *data,
>>  return err;
>>  }
>>
>> +struct raid56_buffer {
>> +  void *buf;
>> +  int  data_is_valid;
>> +};
>> +
>> +static void
>> +rebuild_raid5 (struct raid56_buffer *buffers, grub_uint64_t nstripes,
>> +   grub_uint64_t csize)
>> +{
>> +  grub_uint64_t target = 0, i;
>> +
>> +  while (buffers[target].data_is_valid && target < nstripes)
>> +++target;
>> +
>> +  if (target == nstripes)
>> +{
>> +  grub_dprintf ("btrfs", "called rebuild_raid5(), but all disks are 
>> OK\n");
>> +  return;
>> +}
>> +
>> +  grub_dprintf ("btrfs", "rebuilding raid5 stripe #%" PRIuGRUB_UINT64_T 
>> "\n",
>> +target);
>> +  for (i = 0; i < nstripes; i++)
>> +if (i != target)
>> +  grub_crypto_xor (buffers[target].buf, buffers[target].buf, 
>> buffers[i].buf,
>> +   csize);
>> +}
>> +
>> +static grub_err_t
>> +raid56_read_retry (struct grub_btrfs_data *data,
>> +   struct grub_btrfs_chunk_item *chunk,
>> +   grub_uint64_t stripe_offset, grub_uint64_t stripen,
>> +   grub_uint64_t csize, void *buf)
>> +{
>> +
>> +  struct raid56_buffer *buffers = NULL;
>> +  grub_uint64_t nstripes = grub_le_to_cpu16 (chunk->nstripes);
>> +  grub_uint64_t chunk_type = grub_le_to_cpu64 (chunk->type);
>> +  grub_err_t ret = GRUB_ERR_NONE;
>> +  grub_uint64_t i, failed_devices;
>> +
>> +  buffers = grub_zalloc (sizeof(*buffers) * nstripes);
>> +  if (!buffers)
>> +{
>> +  ret = GRUB_ERR_OUT_OF_MEMORY;
>> +  goto cleanup;
>> +}
>> +
>> +  for (i = 0; i < nstripes; i++)
>> +{
>> +  buffers[i].buf = grub_zalloc (csize);
>> +  if (!buffers[i].buf)
>> +{
>> +  ret = GRUB_ERR_OUT_OF_MEMORY;
>> +  goto cleanup;
>> +}
>> +}
>> +
>> +  for (i = 0; i < nstripes; i++)
>> +{
>> +  struct grub_btrfs_chunk_stripe *stripe;
>> +  grub_disk_addr_t paddr;
>> +  grub_device_t dev;
>> +  grub_err_t err2;
>> +
>> +  stripe = (struct grub_btrfs_chunk_stripe *) (chunk + 1);
>> +  stripe += i;
>> +
>> +  paddr = grub_le_to_cpu64 (stripe->offset) + stripe_offset;
>> +  grub_dprintf ("btrfs", "reading paddr %" PRIxGRUB_UINT64_T
>> +" from stripe ID %" PRIxGRUB_UINT64_T "\n", paddr,
>> +stripe->device_id);
>> +
>> +  dev = find_device (data, stripe->device_id);
>> +  if (!dev)
>> +{
>> +  buffers[i].data_is_valid = 0;
>> +  grub_dprintf ("btrfs", "stripe %" PRIuGRUB_UINT64_T " FAILED (dev ID 
>> %"
>> +PRIxGRUB_UINT64_T ")\n", i, stripe->device_id);
>> +  continue;
> 
> What will happen if more than one stripe is broken?
See below
> 
>> +}
>> +
>> +  err2 = grub_disk_read (dev->disk, paddr >> GRUB_DISK_SECTOR_BITS,
>> + paddr & (GRUB_DISK_SECTOR_SIZE - 1),
>> + csize, buffers[i].buf);
>> +  if (err2 == GRUB_ERR_NONE)
>> +{
>> +  buffers[i].data_is_valid = 1;
>> +  grub_dprintf ("btrfs", "stripe %" PRIuGRUB_UINT64_T " Ok (dev ID %"
>> +PRIxGRUB_UINT64_T ")\n", i, stripe->device_id);
>> +}
>> +  else
>> +{
>> +  buffers[i].data_is_valid = 0;
>> +  grub_dprintf ("btrfs", "stripe %" PRIuGRUB_UINT64_T
>> +" FAILED (dev ID %" PRIxGRUB_UINT64_T ")\n", i,

Re: [PATCH 1/9] btrfs: add support for reading a filesystem with a RAID 5 or RAID 6 profile.

2018-06-01 Thread Goffredo Baroncelli
Hi Daniel,

my comments below
On 05/30/2018 12:07 PM, Daniel Kiper wrote:
> On Wed, May 16, 2018 at 08:48:11PM +0200, Goffredo Baroncelli wrote:
>> Signed-off-by: Goffredo Baroncelli 
[...]

>> +   *  off -> logical address to read (from the beginning of the
>> +   * chunk space)
> 
> What do you mean by "chunk"? Is it e.g. A1 + B1 region? Please make it
> clear what do you mean by "chunk".

Chunk is a very pervasive concept in BTRFS. A reader who looks to a BTRFS
raid code, must know very well this concepts. I am not sure that GRUB code
is the right place to contain a full BTRFS chunk description.

Basically, the BTRFS logical space is mapped to the physical one via the
chunks (aka block group). Each chunk maps a range of the logical address to a
range in the disk(s). If more disks are involved, different profile
might be used (linear, raid0... raid5/6). E.g.: a chunk maps a logical
address (say 0.1GB) to a physical address (say 1GB..2GB of disk1, 3GB..to 
4GB of disk2, in raid1 mode).
The chunks are a low layer. All the BTRFS metadata are in term of the 
logical address (upper layer).

[...]
>> +  /*
>> +   * In the line below stripen is evaluated without considering
> 
> s/In the line below //
> 
>> +   * the parities (0 for A1, A2, A3... 1 for B1, B2...);
> 
> s/;/./
> 
>> +   */
>> +  high = grub_divmod64 (stripe_nr, nstripes - nparities, );
>> +
>> +  /*
>> +   * In the line below stripen, now consider also the parities (0
> 
> s/In the line below stripen, now/Now/

I think that "stripen" must be re-added

> 
>> +   * for A1, 1 for A2, 2 for A3); the math is done "modulo"
> 
> s/; the/. The/
> s/"modulo"/modulo/
> 
>> +   * number of disks
> 
> Full stop at the end of sentence please.
> 
> Daniel
> 


-- 
gpg @keyserver.linux.it: Goffredo Baroncelli 
Key fingerprint BBF5 1610 0B64 DAC6 5F7D  17B2 0EDA 9B37 8B82 E0B5

___
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel


Re: [PATCH 1/8] Add support for reading a filesystem with a raid5 or raid6 profile.

2018-05-16 Thread Goffredo Baroncelli
On 05/14/2018 07:52 PM, Daniel Kiper wrote:
> On Fri, May 11, 2018 at 09:24:39PM +0200, Goffredo Baroncelli wrote:
>> Signed-off-by: Goffredo Baroncelli <kreij...@inwind.it>
>> ---
>>  grub-core/fs/btrfs.c | 61 
>>  1 file changed, 61 insertions(+)
>>
>> diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
>> index be195448d..7e287d0ec 100644
>> --- a/grub-core/fs/btrfs.c
>> +++ b/grub-core/fs/btrfs.c
>> @@ -119,6 +119,8 @@ struct grub_btrfs_chunk_item
>>  #define GRUB_BTRFS_CHUNK_TYPE_RAID1 0x10
>>  #define GRUB_BTRFS_CHUNK_TYPE_DUPLICATED0x20
>>  #define GRUB_BTRFS_CHUNK_TYPE_RAID100x40
>> +#define GRUB_BTRFS_CHUNK_TYPE_RAID5 0x80
>> +#define GRUB_BTRFS_CHUNK_TYPE_RAID6 0x100
>>grub_uint8_t dummy2[0xc];
>>grub_uint16_t nstripes;
>>grub_uint16_t nsubstripes;
>> @@ -764,6 +766,65 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, 
>> grub_disk_addr_t addr,
>>stripe_offset = low + chunk_stripe_length
>>  * high;
>>csize = chunk_stripe_length - low;
>> +  break;
>> +}
>> +  case GRUB_BTRFS_CHUNK_TYPE_RAID5:
>> +  case GRUB_BTRFS_CHUNK_TYPE_RAID6:
>> +{
>> +  grub_uint64_t nparities, stripe_nr, high, low;
>> +
>> +  redundancy = 1;   /* no redundancy for now */
>> +
>> +  if (grub_le_to_cpu64 (chunk->type) & GRUB_BTRFS_CHUNK_TYPE_RAID5)
>> +{
>> +  grub_dprintf ("btrfs", "RAID5\n");
>> +  nparities = 1;
>> +}
>> +  else
>> +{
>> +  grub_dprintf ("btrfs", "RAID6\n");
>> +  nparities = 2;
>> +}
>> +
>> +  /*
>> +   * A raid 6 layout consists in several stripes spread
> 
> This line is confusing if you compare it with both cases a few lines above.
> I think that you should explain somewhere why this math works for RAID 5
> and RAID 6.
> 
>> +   * on the disks, following a layout like the one below
>> +   *
>> +   *   Disk1  Disk2  Disk3  Ddisk4
>> +   *
>> +   *A1 B1 P1 Q1
>> +   *Q2 A2 B2 P2
>> +   *P3 Q3 A3 B3
>> +   *  [...]
>> +   *
>> +   *  Note that the placement of the parities depends by the row
> 
> s/by the/on/?
> 
>> +   *  index;
>> +   *  In the code below:
>> +   *  stripe_nr -> is the stripe number not considering the parities
>> +   *   (A1=0, B1=1, A2 = 2, B2 = 3, ...)
>> +   *  high -> is the row number (0 for A1...Q1, 1 for Q2..P2, ...)
>> +   *  stripen -> is the column number (or number of disk)
> 
> s/number of disk/number of disk in row/?
> 
>> +   *  off -> logical address to read (from teh beginning of the
> 
> What is "teh"? nth? And it seems to me that off is the offset from the
> beginning of the array.
> 
>> +   * chunk space)
> 
> I am not sure what you mean by "chunk space" here.

In BTRFS the logical space where are stored the data and the metadata is 
divided in chunks (or block group). Each chunk maps its "logical space" to the
disk(s) space; how this "logical space" is arranged on the disk(s) depends by
the chunk profile. Eg:
- RAID1: the logical space is mapped on two disks, where each disk is the mirror
  of the other.
- RAID5: the logical space is mapped on (n-1) disks, and the Nth disk is used
  as parity (where n is the number of available disks).

Each chunks maps a logical space that has a size of few GB. This means that
a btrfs filesystem has several chunks. And each chunks may have different 
profiles.
So it is possible that inside a btrfs filesystem there are area where a RAID1 
profile is applied, other where a RAID5 is applied and so on...


> 
>> +   *  chunk_stripe_length -> size of a stripe (typically 64k)
> 
> I assume that stripe does not cover P1 and Q1 (parity disks)?

a stripe is a slice of a "row" (e.g. A1, B2, Q3...)
> 
>> +   *  nstripes -> number of disks
>> +   *
>> +   */
>> +  stripe_nr = grub_divmod64 (off, chunk_stripe_length, );
> 
> What is the low?

"low", is the offset where the data is placed inside a "stripe"


> 
>> +  high = grub_divmod64 (stripe_nr, nstripe

Re: [PATCH 8/8] Add raid6 recovery for a btrfs filesystem.

2018-05-16 Thread Goffredo Baroncelli
On 05/14/2018 09:06 PM, Daniel Kiper wrote:
> On Fri, May 11, 2018 at 09:24:46PM +0200, Goffredo Baroncelli wrote:
>> Add the raid6 recovery, in order to use a raid6 filesystem even if some
>> disks (up to two) are missing.
>> This code use the old md raid6 code already present in grub.
> 
> Please fix the commit message formating.
> 
>> Signed-off-by: Goffredo Baroncelli <kreij...@inwind.it>
>> ---
>>  grub-core/fs/btrfs.c | 43 +++
>>  1 file changed, 39 insertions(+), 4 deletions(-)
>>
>> diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
>> index 8d72607d1..07e9db910 100644
>> --- a/grub-core/fs/btrfs.c
>> +++ b/grub-core/fs/btrfs.c
>> @@ -30,6 +30,7 @@
>>  #include 
>>  #include 
>>  #include 
>> +#include 
>>
>>  GRUB_MOD_LICENSE ("GPLv3+");
>>
>> @@ -696,11 +697,36 @@ rebuild_raid5 (struct raid56_buffer *buffers, 
>> grub_uint64_t nstripes,
>> csize);
>>  }
>>
>> +static grub_err_t
>> +raid_recover_read_raid56_buffer (void *data, int disk_nr, grub_uint64_t 
>> addr,
>> + void *dest, grub_size_t size)
> 
> s/raid_recover_read_raid56_buffer/raid6_recover_read_node/
I renamed this as raid6_recover_read_buffer
It is not a node, because it is different handler than the one of patch #7


> 
>> +{
>> +struct raid56_buffer *buffers = data;
>> +
>> +(void)addr;
> 
> I do not like this. grub_uint64_t addr __attribute__ ((unused))" in
> prototype definition please.
> 
>> +grub_memcpy(dest, buffers[disk_nr].buf, size);
>> +
>> +grub_errno = buffers[disk_nr].data_is_valid ? GRUB_ERR_NONE :
>> + GRUB_ERR_READ_ERROR;
>> +return grub_errno;
>> +}
>> +
>> +static void
>> +rebuild_raid6 (struct raid56_buffer *buffers, grub_uint64_t nstripes,
>> +   grub_uint64_t csize, grub_uint64_t parities_pos, void *dest,
>> +   grub_uint64_t stripen)
> 
> struct as a argument?

I thought a bit about that; however I think that the there too few place where 
grub_raid6_recover_gen() is called, so my feeling is that the effort is more
than the gain.
> 
>> +
>> +{
>> +  grub_raid6_recover_generic (buffers, nstripes, stripen, parities_pos,
>> +  dest, 0, csize,
>> +  raid_recover_read_raid56_buffer, 0);
>> +}
>> +
>>  static grub_err_t
>>  raid56_read_retry (struct grub_btrfs_data *data,
>> struct grub_btrfs_chunk_item *chunk,
>> grub_uint64_t stripe_offset, grub_uint64_t stripen,
>> -   grub_uint64_t csize, void *buf)
>> +   grub_uint64_t csize, void *buf, grub_uint64_t parities_pos)
> 
> struct as a argument?
> 
> Daniel
> 
> ___
> Grub-devel mailing list
> Grub-devel@gnu.org
> https://lists.gnu.org/mailman/listinfo/grub-devel
> 


-- 
gpg @keyserver.linux.it: Goffredo Baroncelli 
Key fingerprint BBF5 1610 0B64 DAC6 5F7D  17B2 0EDA 9B37 8B82 E0B5

___
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel


[PATCH 5/9] btrfs: move logging code in grub_btrfs_read_logical()

2018-05-16 Thread Goffredo Baroncelli
A portion of the logging code is moved outside a for(;;). The part that is
left inside is the one which depends by the for(;;) index.
This is a preparatory patch: in the next one it will be possible to refactor
the code inside the for(;;) in an another function.
---
 grub-core/fs/btrfs.c | 25 ++---
 1 file changed, 14 insertions(+), 11 deletions(-)

diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
index 4920dd3b1..51f300829 100644
--- a/grub-core/fs/btrfs.c
+++ b/grub-core/fs/btrfs.c
@@ -865,6 +865,18 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, 
grub_disk_addr_t addr,
 
for (j = 0; j < 2; j++)
  {
+   grub_dprintf ("btrfs", "chunk 0x%" PRIxGRUB_UINT64_T
+ "+0x%" PRIxGRUB_UINT64_T
+ " (%d stripes (%d substripes) of %"
+ PRIxGRUB_UINT64_T ") \n",
+ grub_le_to_cpu64 (key->offset),
+ grub_le_to_cpu64 (chunk->size),
+ grub_le_to_cpu16 (chunk->nstripes),
+ grub_le_to_cpu16 (chunk->nsubstripes),
+ grub_le_to_cpu64 (chunk->stripe_length));
+   grub_dprintf ("btrfs", "reading laddr 0x%" PRIxGRUB_UINT64_T "\n",
+ addr);
+
for (i = 0; i < redundancy; i++)
  {
struct grub_btrfs_chunk_stripe *stripe;
@@ -877,20 +889,11 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, 
grub_disk_addr_t addr,
 
paddr = grub_le_to_cpu64 (stripe->offset) + stripe_offset;
 
-   grub_dprintf ("btrfs", "chunk 0x%" PRIxGRUB_UINT64_T
- "+0x%" PRIxGRUB_UINT64_T
- " (%d stripes (%d substripes) of %"
- PRIxGRUB_UINT64_T ") stripe %" PRIxGRUB_UINT64_T
+   grub_dprintf ("btrfs", "stripe %" PRIxGRUB_UINT64_T
  " maps to 0x%" PRIxGRUB_UINT64_T "\n",
- grub_le_to_cpu64 (key->offset),
- grub_le_to_cpu64 (chunk->size),
- grub_le_to_cpu16 (chunk->nstripes),
- grub_le_to_cpu16 (chunk->nsubstripes),
- grub_le_to_cpu64 (chunk->stripe_length),
  stripen, stripe->offset);
grub_dprintf ("btrfs", "reading paddr 0x%" PRIxGRUB_UINT64_T
- " for laddr 0x%" PRIxGRUB_UINT64_T "\n", paddr,
- addr);
+ "\n", paddr);
 
dev = find_device (data, stripe->device_id);
if (!dev)
-- 
2.17.0


___
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel


[PATCH 8/9] btrfs: make more generic the code for RAID 6 rebuilding

2018-05-16 Thread Goffredo Baroncelli
The original code which handles the recovery of a RAID 6 disks array
assumes that all reads are multiple of 1 << GRUB_DISK_SECTOR_BITS and it
assumes that all the I/O is done via the struct grub_diskfilter_segment.
This is not true for the btrfs code. In order to reuse the native
grub_raid6_recover() code, it is modified to not call
grub_diskfilter_read_node() directly, but to call an handler passed
as argument.

Signed-off-by: Goffredo Baroncelli <kreij...@inwind.it>
---
 grub-core/disk/raid6_recover.c | 53 ++
 include/grub/diskfilter.h  |  9 ++
 2 files changed, 44 insertions(+), 18 deletions(-)

diff --git a/grub-core/disk/raid6_recover.c b/grub-core/disk/raid6_recover.c
index aa674f6ca..10ee495e2 100644
--- a/grub-core/disk/raid6_recover.c
+++ b/grub-core/disk/raid6_recover.c
@@ -74,14 +74,25 @@ mod_255 (unsigned x)
 }
 
 static grub_err_t
-grub_raid6_recover (struct grub_diskfilter_segment *array, int disknr, int p,
-char *buf, grub_disk_addr_t sector, grub_size_t size)
+raid6_recover_read_node (void *data, int disk_nr,
+   grub_uint64_t sector,
+   void *buf, grub_size_t size)
+{
+struct grub_diskfilter_segment *array = data;
+return grub_diskfilter_read_node (>nodes[disk_nr],
+ (grub_disk_addr_t)sector,
+ size >> GRUB_DISK_SECTOR_BITS, buf);
+}
+
+grub_err_t
+grub_raid6_recover_gen (void *data, grub_uint64_t nstripes, int disknr, int p,
+   char *buf, grub_uint64_t sector, grub_size_t size,
+   raid_recover_read_t read_func, int layout)
 {
   int i, q, pos;
   int bad1 = -1, bad2 = -1;
   char *pbuf = 0, *qbuf = 0;
 
-  size <<= GRUB_DISK_SECTOR_BITS;
   pbuf = grub_zalloc (size);
   if (!pbuf)
 goto quit;
@@ -91,17 +102,17 @@ grub_raid6_recover (struct grub_diskfilter_segment *array, 
int disknr, int p,
 goto quit;
 
   q = p + 1;
-  if (q == (int) array->node_count)
+  if (q == (int) nstripes)
 q = 0;
 
   pos = q + 1;
-  if (pos == (int) array->node_count)
+  if (pos == (int) nstripes)
 pos = 0;
 
-  for (i = 0; i < (int) array->node_count - 2; i++)
+  for (i = 0; i < (int) nstripes - 2; i++)
 {
   int c;
-  if (array->layout & GRUB_RAID_LAYOUT_MUL_FROM_POS)
+  if (layout & GRUB_RAID_LAYOUT_MUL_FROM_POS)
c = pos;
   else
c = i;
@@ -109,8 +120,7 @@ grub_raid6_recover (struct grub_diskfilter_segment *array, 
int disknr, int p,
 bad1 = c;
   else
 {
-  if (! grub_diskfilter_read_node (>nodes[pos], sector,
-  size >> GRUB_DISK_SECTOR_BITS, buf))
+ if (!read_func(data, pos, sector, buf, size))
 {
   grub_crypto_xor (pbuf, pbuf, buf, size);
   grub_raid_block_mulx (c, buf, size);
@@ -128,7 +138,7 @@ grub_raid6_recover (struct grub_diskfilter_segment *array, 
int disknr, int p,
 }
 
   pos++;
-  if (pos == (int) array->node_count)
+  if (pos == (int) nstripes)
 pos = 0;
 }
 
@@ -139,16 +149,14 @@ grub_raid6_recover (struct grub_diskfilter_segment 
*array, int disknr, int p,
   if (bad2 < 0)
 {
   /* One bad device */
-  if ((! grub_diskfilter_read_node (>nodes[p], sector,
-   size >> GRUB_DISK_SECTOR_BITS, buf)))
+  if (!read_func(data, p, sector, buf, size))
 {
   grub_crypto_xor (buf, buf, pbuf, size);
   goto quit;
 }
 
   grub_errno = GRUB_ERR_NONE;
-  if (grub_diskfilter_read_node (>nodes[q], sector,
-size >> GRUB_DISK_SECTOR_BITS, buf))
+  if (read_func(data, q, sector, buf, size))
 goto quit;
 
   grub_crypto_xor (buf, buf, qbuf, size);
@@ -160,14 +168,12 @@ grub_raid6_recover (struct grub_diskfilter_segment 
*array, int disknr, int p,
   /* Two bad devices */
   unsigned c;
 
-  if (grub_diskfilter_read_node (>nodes[p], sector,
-size >> GRUB_DISK_SECTOR_BITS, buf))
+  if (read_func(data, p, sector, buf, size))
 goto quit;
 
   grub_crypto_xor (pbuf, pbuf, buf, size);
 
-  if (grub_diskfilter_read_node (>nodes[q], sector,
-size >> GRUB_DISK_SECTOR_BITS, buf))
+  if (read_func(data, q, sector, buf, size))
 goto quit;
 
   grub_crypto_xor (qbuf, qbuf, buf, size);
@@ -190,6 +196,17 @@ quit:
   return grub_errno;
 }
 
+static grub_err_t
+grub_raid6_recover (struct grub_diskfilter_segment *array, int disknr, int p,
+char *buf, grub_disk_addr_t sector, grub_size_t size)
+{
+
+  return grub_raid6_recover_gen (array, array->node_count, disknr, p, buf,
+

Re: [PATCH 6/8] Add support for recovery for a raid5 btrfs profiles.

2018-05-16 Thread Goffredo Baroncelli
On 05/14/2018 08:40 PM, Daniel Kiper wrote:
> On Fri, May 11, 2018 at 09:24:44PM +0200, Goffredo Baroncelli wrote:
>> Signed-off-by: Goffredo Baroncelli <kreij...@inwind.it>
>> ---
>>  grub-core/fs/btrfs.c | 178 +--
>>  1 file changed, 173 insertions(+), 5 deletions(-)
>>
>> diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
>> index fc4198e39..8d72607d1 100644
>> --- a/grub-core/fs/btrfs.c
>> +++ b/grub-core/fs/btrfs.c
>> @@ -29,6 +29,7 @@
>>  #include 
>>  #include 
>>  #include 
>> +#include 
>>
>>  GRUB_MOD_LICENSE ("GPLv3+");
>>
>> @@ -663,9 +664,156 @@ btrfs_read_from_chunk (struct grub_btrfs_data *data,
>>  err = grub_disk_read (dev->disk, paddr >> GRUB_DISK_SECTOR_BITS,
>>paddr & (GRUB_DISK_SECTOR_SIZE - 1),
>>csize, buf);
>> +grub_dprintf ("btrfs", "reading paddr 0x%" PRIxGRUB_UINT64_T "\n", 
>> paddr);
>>  return err;
>>  }
>>
>> +struct raid56_buffer {
>> +  void *buf;
>> +  int  data_is_valid;
>> +};
>> +
>> +static void
>> +rebuild_raid5 (struct raid56_buffer *buffers, grub_uint64_t nstripes,
>> +   grub_uint64_t csize)
>> +{
>> +  grub_uint64_t target = 0, i;
>> +
>> +  while (buffers[target].data_is_valid && target < nstripes)
>> +++target;
>> +
>> +  if (target == nstripes)
>> +{
>> +  grub_dprintf ("btrfs", "called rebuild_raid5(), but all disks are 
>> OK\n");
>> +  return;
>> +}
>> +
>> +  grub_dprintf ("btrfs", "rebuilding raid5 stripe #%" PRIuGRUB_UINT64_T 
>> "\n",
>> +target);
>> +  for (i = 0; i < nstripes ; i++)
> 
> Please drop extra space behind nstripes. I have found
> similar issues below too. Please fix all of them.
> 
>> +if (i != target)
>> +  grub_crypto_xor (buffers[target].buf, buffers[target].buf, 
>> buffers[i].buf,
>> +   csize);
>> +}
>> +
>> +static grub_err_t
>> +raid56_read_retry (struct grub_btrfs_data *data,
>> +   struct grub_btrfs_chunk_item *chunk,
>> +   grub_uint64_t stripe_offset, grub_uint64_t stripen,
>> +   grub_uint64_t csize, void *buf)
>> +{
>> +
>> +  struct raid56_buffer *buffers = NULL;
>> +  grub_uint64_t nstripes = grub_le_to_cpu16 (chunk->nstripes);
>> +  grub_uint64_t chunk_type = grub_le_to_cpu64 (chunk->type);
>> +  grub_err_t ret = GRUB_ERR_NONE;
>> +  grub_uint64_t i, failed_devices;
>> +
>> +  buffers = grub_zalloc (sizeof(*buffers) * nstripes);
>> +  if (!buffers)
>> +{
>> +  ret = GRUB_ERR_OUT_OF_MEMORY;
>> +  goto cleanup;
>> +}
>> +
>> +  for (i = 0; i < nstripes ; i++)
> 
> Ditto.
> 
>> +{
>> +  buffers[i].buf = grub_zalloc (csize);
>> +  if (!buffers[i].buf)
>> +{
>> +  ret = GRUB_ERR_OUT_OF_MEMORY;
>> +  goto cleanup;
>> +}
>> +}
>> +
>> +  for (i = 0; i < nstripes ; i++)
> 
> Ditto.
> 
>> +{
>> +  struct grub_btrfs_chunk_stripe *stripe;
>> +  grub_disk_addr_t paddr;
>> +  grub_device_t dev;
>> +  grub_err_t err2;
>> +
>> +  stripe = (struct grub_btrfs_chunk_stripe *) (chunk + 1);
>> +  stripe += i;
>> +
>> +  paddr = grub_le_to_cpu64 (stripe->offset) + stripe_offset;
>> +  grub_dprintf ("btrfs", "reading paddr %" PRIxGRUB_UINT64_T
>> +" from stripe ID %" PRIxGRUB_UINT64_T "\n", paddr,
>> +stripe->device_id);
>> +
>> +  /* FIXME: rescan the devices */
> 
> Could you be more verbose here?
This was an initial comment. After few thoughts I concluded that it was wrong. 
So I 
removed it in the next patches set.

> 
>> +  dev = find_device (data, stripe->device_id);
>> +  if (!dev)
>> +{
>> +  buffers[i].data_is_valid = 0;
>> +  grub_dprintf ("btrfs", "stripe %" PRIuGRUB_UINT64_T " FAILED (dev ID 
>> %"
>> +PRIxGRUB_UINT64_T ")\n", i, stripe->device_id);
>> +  continue;
>> +}
>> +
>> +  err2 = grub_disk_read (dev->disk, paddr >> GRUB_DISK_SECTOR_BITS,
>> + paddr & (GRUB_

[PATCH 2/9] btrfs: add helper to check the btrfs header.

2018-05-16 Thread Goffredo Baroncelli
This helper will be used in few places to help the debugging. As
conservative approach, in case of error it is only logged. This
because I am not sure if this can change something in the error
handling of the currently existing code.

Signed-off-by: Goffredo Baroncelli <kreij...@inwind.it>
---
 grub-core/fs/btrfs.c | 24 +++-
 1 file changed, 23 insertions(+), 1 deletion(-)

diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
index 394611cbb..123bfbfc1 100644
--- a/grub-core/fs/btrfs.c
+++ b/grub-core/fs/btrfs.c
@@ -77,7 +77,8 @@ struct btrfs_header
 {
   grub_btrfs_checksum_t checksum;
   grub_btrfs_uuid_t uuid;
-  grub_uint8_t dummy[0x30];
+  grub_uint64_t bytenr;
+  grub_uint8_t dummy[0x28];
   grub_uint32_t nitems;
   grub_uint8_t level;
 } GRUB_PACKED;
@@ -286,6 +287,25 @@ free_iterator (struct grub_btrfs_leaf_descriptor *desc)
   grub_free (desc->data);
 }
 
+static grub_err_t
+check_btrfs_header (struct grub_btrfs_data *data, struct btrfs_header *header,
+grub_disk_addr_t addr)
+{
+  if (grub_le_to_cpu64 (header->bytenr) != addr)
+{
+  grub_dprintf ("btrfs", "btrfs_header.bytenr is not equal node addr\n");
+  return grub_error (GRUB_ERR_BAD_FS,
+"header bytenr is not equal node addr");
+}
+  if (grub_memcmp (data->sblock.uuid, header->uuid, sizeof(grub_btrfs_uuid_t)))
+{
+  grub_dprintf ("btrfs", "btrfs_header.uuid doesn't match sblock uuid\n");
+  return grub_error (GRUB_ERR_BAD_FS,
+"header uuid doesn't match sblock uuid");
+}
+  return GRUB_ERR_NONE;
+}
+
 static grub_err_t
 save_ref (struct grub_btrfs_leaf_descriptor *desc,
  grub_disk_addr_t addr, unsigned i, unsigned m, int l)
@@ -341,6 +361,7 @@ next (struct grub_btrfs_data *data,
 
   err = grub_btrfs_read_logical (data, grub_le_to_cpu64 (node.addr),
 , sizeof (head), 0);
+  check_btrfs_header (data, , grub_le_to_cpu64 (node.addr));
   if (err)
return -err;
 
@@ -402,6 +423,7 @@ lower_bound (struct grub_btrfs_data *data,
   /* FIXME: preread few nodes into buffer. */
   err = grub_btrfs_read_logical (data, addr, , sizeof (head),
 recursion_depth + 1);
+  check_btrfs_header (data, , addr);
   if (err)
return err;
   addr += sizeof (head);
-- 
2.17.0


___
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel


[PATCH V4] Add support for BTRFS raid5/6 to GRUB

2018-05-16 Thread Goffredo Baroncelli

Hi All,

the aim of this patches set is to provide support for a BTRFS raid5/6
filesystem in GRUB.

The first patch, implements the basic support for raid5/6. I.e this works when
all the disks are present.

The next 5 patches, are preparatory ones.

The 7th patch implements the raid5 recovery for btrfs (i.e. handling the
disappearing of 1 disk).
The 8th patch makes the code for handling the raid6 recovery more generic.
The last one implements the raid6 recovery for btrfs (i.e. handling the
disappearing up to two disks).

I tested the code in grub-emu, and it works (for me) both with all the disks,
and with some disks missing. I checked the crc32 calculated from grub and
from linux and these matched. Finally I checked if the support for md raid6
still works properly, and it does (with all the drives and with up to 2 drives
missing)

Comments are welcome.

Changelog
v1: initial support for btrfs raid5/6. No recovery allowed
v2: full support for btrfs raid5/6. Recovery allowed
v3: some minor cleanup suggested by Daniel Kiper; reusing the
original raid6 recovery code of grub
v4: Several spell fix; better description of the RAID layout
in btrfs, and the variables which describes the stripe
positioning; split the patch #5 in two (#5 and #6)


BR
G.Baroncelli--
gpg @keyserver.linux.it: Goffredo Baroncelli 
Key fingerprint BBF5 1610 0B64 DAC6 5F7D  17B2 0EDA 9B37 8B82 E0B5



___
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel


[PATCH 7/9] btrfs: add support for recovery for a RAID 5 btrfs profiles.

2018-05-16 Thread Goffredo Baroncelli
Signed-off-by: Goffredo Baroncelli <kreij...@inwind.it>
---
 grub-core/fs/btrfs.c | 174 ++-
 1 file changed, 170 insertions(+), 4 deletions(-)

diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
index 63651928b..5fcaad86f 100644
--- a/grub-core/fs/btrfs.c
+++ b/grub-core/fs/btrfs.c
@@ -29,6 +29,7 @@
 #include 
 #include 
 #include 
+#include 
 
 GRUB_MOD_LICENSE ("GPLv3+");
 
@@ -666,6 +667,150 @@ btrfs_read_from_chunk (struct grub_btrfs_data *data,
 return err;
 }
 
+struct raid56_buffer {
+  void *buf;
+  int  data_is_valid;
+};
+
+static void
+rebuild_raid5 (struct raid56_buffer *buffers, grub_uint64_t nstripes,
+   grub_uint64_t csize)
+{
+  grub_uint64_t target = 0, i;
+
+  while (buffers[target].data_is_valid && target < nstripes)
+++target;
+
+  if (target == nstripes)
+{
+  grub_dprintf ("btrfs", "called rebuild_raid5(), but all disks are OK\n");
+  return;
+}
+
+  grub_dprintf ("btrfs", "rebuilding raid5 stripe #%" PRIuGRUB_UINT64_T "\n",
+   target);
+  for (i = 0; i < nstripes; i++)
+if (i != target)
+  grub_crypto_xor (buffers[target].buf, buffers[target].buf, 
buffers[i].buf,
+   csize);
+}
+
+static grub_err_t
+raid56_read_retry (struct grub_btrfs_data *data,
+  struct grub_btrfs_chunk_item *chunk,
+  grub_uint64_t stripe_offset, grub_uint64_t stripen,
+  grub_uint64_t csize, void *buf)
+{
+
+  struct raid56_buffer *buffers = NULL;
+  grub_uint64_t nstripes = grub_le_to_cpu16 (chunk->nstripes);
+  grub_uint64_t chunk_type = grub_le_to_cpu64 (chunk->type);
+  grub_err_t ret = GRUB_ERR_NONE;
+  grub_uint64_t i, failed_devices;
+
+  buffers = grub_zalloc (sizeof(*buffers) * nstripes);
+  if (!buffers)
+{
+  ret = GRUB_ERR_OUT_OF_MEMORY;
+  goto cleanup;
+}
+
+  for (i = 0; i < nstripes; i++)
+{
+  buffers[i].buf = grub_zalloc (csize);
+  if (!buffers[i].buf)
+   {
+ ret = GRUB_ERR_OUT_OF_MEMORY;
+ goto cleanup;
+   }
+}
+
+  for (i = 0; i < nstripes; i++)
+{
+  struct grub_btrfs_chunk_stripe *stripe;
+  grub_disk_addr_t paddr;
+  grub_device_t dev;
+  grub_err_t err2;
+
+  stripe = (struct grub_btrfs_chunk_stripe *) (chunk + 1);
+  stripe += i;
+
+  paddr = grub_le_to_cpu64 (stripe->offset) + stripe_offset;
+  grub_dprintf ("btrfs", "reading paddr %" PRIxGRUB_UINT64_T
+" from stripe ID %" PRIxGRUB_UINT64_T "\n", paddr,
+stripe->device_id);
+
+  dev = find_device (data, stripe->device_id);
+  if (!dev)
+   {
+ buffers[i].data_is_valid = 0;
+ grub_dprintf ("btrfs", "stripe %" PRIuGRUB_UINT64_T " FAILED (dev ID 
%"
+   PRIxGRUB_UINT64_T ")\n", i, stripe->device_id);
+ continue;
+   }
+
+  err2 = grub_disk_read (dev->disk, paddr >> GRUB_DISK_SECTOR_BITS,
+paddr & (GRUB_DISK_SECTOR_SIZE - 1),
+csize, buffers[i].buf);
+  if (err2 == GRUB_ERR_NONE)
+   {
+ buffers[i].data_is_valid = 1;
+ grub_dprintf ("btrfs", "stripe %" PRIuGRUB_UINT64_T " Ok (dev ID %"
+   PRIxGRUB_UINT64_T ")\n", i, stripe->device_id);
+   }
+  else
+   {
+ buffers[i].data_is_valid = 0;
+ grub_dprintf ("btrfs", "stripe %" PRIuGRUB_UINT64_T
+   " FAILED (dev ID %" PRIxGRUB_UINT64_T ")\n", i,
+   stripe->device_id);
+   }
+}
+
+  failed_devices = 0;
+  for (i = 0; i < nstripes; i++)
+if (!buffers[i].data_is_valid)
+  ++failed_devices;
+  if (failed_devices > 1 && (chunk_type & GRUB_BTRFS_CHUNK_TYPE_RAID5))
+{
+  grub_dprintf ("btrfs",
+   "not enough disks for raid5: total %" PRIuGRUB_UINT64_T
+   ", missing %" PRIuGRUB_UINT64_T "\n",
+   nstripes, failed_devices);
+  ret = GRUB_ERR_READ_ERROR;
+  goto cleanup;
+}
+  else
+{
+  grub_dprintf ("btrfs",
+"enough disks for raid5/6 rebuilding: total %"
+   PRIuGRUB_UINT64_T ", missing %" PRIuGRUB_UINT64_T "\n",
+nstripes, failed_devices);
+}
+
+  /* if these are enough, try to rebuild the data */
+  if (chunk_type & GRUB_BTRFS_CHUNK_TYPE_RAID5)
+{
+  rebuild_raid5 (buffers, nstripes, csize);
+  grub_memcpy (buf, buffers[stripen].buf, csize);
+}
+  else
+{
+  grub_dprintf ("btrfs", "called rebuild_ra

[PATCH 9/9] btrfs: add RAID 6 recovery for a btrfs filesystem.

2018-05-16 Thread Goffredo Baroncelli
Add the RAID 6 recovery, in order to use a RAID 6 filesystem even if some
disks (up to two) are missing. This code use the old md RAID 6 code already
present in grub.

Signed-off-by: Goffredo Baroncelli <kreij...@inwind.it>
---
 grub-core/fs/btrfs.c | 45 +++-
 1 file changed, 40 insertions(+), 5 deletions(-)

diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
index 5fcaad86f..3d71b954e 100644
--- a/grub-core/fs/btrfs.c
+++ b/grub-core/fs/btrfs.c
@@ -30,6 +30,7 @@
 #include 
 #include 
 #include 
+#include 
 
 GRUB_MOD_LICENSE ("GPLv3+");
 
@@ -695,11 +696,35 @@ rebuild_raid5 (struct raid56_buffer *buffers, 
grub_uint64_t nstripes,
csize);
 }
 
+static grub_err_t
+raid6_recover_read_buffer (void *data, int disk_nr,
+  grub_uint64_t addr __attribute__ ((unused)),
+  void *dest, grub_size_t size)
+{
+struct raid56_buffer *buffers = data;
+
+grub_memcpy(dest, buffers[disk_nr].buf, size);
+
+grub_errno = buffers[disk_nr].data_is_valid ? GRUB_ERR_NONE : 
+GRUB_ERR_READ_ERROR;
+return grub_errno;
+}
+
+static void
+rebuild_raid6 (struct raid56_buffer *buffers, grub_uint64_t nstripes,
+   grub_uint64_t csize, grub_uint64_t parities_pos, void *dest,
+   grub_uint64_t stripen)
+
+{
+  grub_raid6_recover_gen (buffers, nstripes, stripen, parities_pos,
+  dest, 0, csize, raid6_recover_read_buffer, 0);
+}
+
 static grub_err_t
 raid56_read_retry (struct grub_btrfs_data *data,
   struct grub_btrfs_chunk_item *chunk,
   grub_uint64_t stripe_offset, grub_uint64_t stripen,
-  grub_uint64_t csize, void *buf)
+  grub_uint64_t csize, void *buf, grub_uint64_t parities_pos)
 {
 
   struct raid56_buffer *buffers = NULL;
@@ -780,6 +805,15 @@ raid56_read_retry (struct grub_btrfs_data *data,
   ret = GRUB_ERR_READ_ERROR;
   goto cleanup;
 }
+  else if (failed_devices > 2 && (chunk_type & GRUB_BTRFS_CHUNK_TYPE_RAID6))
+{
+  grub_dprintf ("btrfs",
+   "not enough disks for raid6: total %" PRIuGRUB_UINT64_T
+   ", missing %" PRIuGRUB_UINT64_T "\n",
+   nstripes, failed_devices);
+  ret = GRUB_ERR_READ_ERROR;
+  goto cleanup;
+}
   else
 {
   grub_dprintf ("btrfs",
@@ -796,7 +830,7 @@ raid56_read_retry (struct grub_btrfs_data *data,
 }
   else
 {
-  grub_dprintf ("btrfs", "called rebuild_raid6(), NOT IMPLEMENTED\n");
+  rebuild_raid6 (buffers, nstripes, csize, parities_pos, buf, stripen);
 }
 
 cleanup:
@@ -891,8 +925,9 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, 
grub_disk_addr_t addr,
int is_raid56;
grub_uint64_t parities_pos = 0;
 
-   is_raid56 = !!(grub_le_to_cpu64 (chunk->type) &
-  GRUB_BTRFS_CHUNK_TYPE_RAID5);
+is_raid56 = !!(grub_le_to_cpu64 (chunk->type) &
+  (GRUB_BTRFS_CHUNK_TYPE_RAID5|
+   GRUB_BTRFS_CHUNK_TYPE_RAID6));
 
if (grub_le_to_cpu64 (chunk->size) <= off)
  {
@@ -1089,7 +1124,7 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, 
grub_disk_addr_t addr,
 csize, buf);
if (err != GRUB_ERR_NONE)
  err = raid56_read_retry (data, chunk, stripe_offset,
-  stripen, csize, buf);
+  stripen, csize, buf, parities_pos);
  }
 
if (err == GRUB_ERR_NONE)
-- 
2.17.0


___
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel


[PATCH 1/9] btrfs: add support for reading a filesystem with a RAID 5 or RAID 6 profile.

2018-05-16 Thread Goffredo Baroncelli
Signed-off-by: Goffredo Baroncelli <kreij...@inwind.it>
---
 grub-core/fs/btrfs.c | 68 
 1 file changed, 68 insertions(+)

diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
index be195448d..394611cbb 100644
--- a/grub-core/fs/btrfs.c
+++ b/grub-core/fs/btrfs.c
@@ -119,6 +119,8 @@ struct grub_btrfs_chunk_item
 #define GRUB_BTRFS_CHUNK_TYPE_RAID1 0x10
 #define GRUB_BTRFS_CHUNK_TYPE_DUPLICATED0x20
 #define GRUB_BTRFS_CHUNK_TYPE_RAID100x40
+#define GRUB_BTRFS_CHUNK_TYPE_RAID5 0x80
+#define GRUB_BTRFS_CHUNK_TYPE_RAID6 0x100
   grub_uint8_t dummy2[0xc];
   grub_uint16_t nstripes;
   grub_uint16_t nsubstripes;
@@ -764,6 +766,72 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, 
grub_disk_addr_t addr,
  stripe_offset = low + chunk_stripe_length
* high;
  csize = chunk_stripe_length - low;
+ break;
+   }
+ case GRUB_BTRFS_CHUNK_TYPE_RAID5:
+ case GRUB_BTRFS_CHUNK_TYPE_RAID6:
+   {
+ grub_uint64_t nparities, stripe_nr, high, low;
+
+ redundancy = 1;   /* no redundancy for now */
+
+ if (grub_le_to_cpu64 (chunk->type) & GRUB_BTRFS_CHUNK_TYPE_RAID5)
+   {
+ grub_dprintf ("btrfs", "RAID5\n");
+ nparities = 1;
+   }
+ else
+   {
+ grub_dprintf ("btrfs", "RAID6\n");
+ nparities = 2;
+   }
+
+ /*
+  * Below an example of a RAID6 layout and the meaning of the
+  * variables. The same apply to RAID5. The only differences is 
that
+  * there is only one parity instead of two.
+  *
+  * A RAID6 6 layout consists in several stripes spread
+  * on the disks, following a layout like the one below
+  *
+  *   Disk1  Disk2  Disk3  Ddisk4
+  *
+  *A1 B1 P1 Q1
+  *Q2 A2 B2 P2  
+  *P3 Q3 A3 B3
+  *  [...]
+  *
+  *  Note that the placement of the parities depends on row index;
+  *  In the code below:
+  *  stripe_nr -> is the stripe number not considering the parities
+  *   (A1=0, B1=1, A2 = 2, B2 = 3, ...)
+  *  high -> is the row number (0 for A1...Q1, 1 for Q2..P2, ...)
+  *  stripen -> is the column number (or disk number)
+  *  off -> logical address to read (from the beginning of the
+  * chunk space)
+  *  chunk_stripe_length -> size of a stripe (typically 64k)
+  *  nstripes -> number of disks
+  *  low -> offset of the data inside a stripe
+  * 
+  */
+ stripe_nr = grub_divmod64 (off, chunk_stripe_length, );
+
+ /*
+  * In the line below stripen is evaluated without considering
+  * the parities (0 for A1, A2, A3... 1 for B1, B2...);
+  */
+ high = grub_divmod64 (stripe_nr, nstripes - nparities, );
+
+ /*
+  * In the line below stripen, now consider also the parities (0
+  * for A1, 1 for A2, 2 for A3); the math is done "modulo"
+  * number of disks
+  */
+ grub_divmod64 (high + stripen, nstripes, );
+
+ stripe_offset = low + chunk_stripe_length * high;
+ csize = chunk_stripe_length - low;
+
  break;
}
  default:
-- 
2.17.0


___
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel


  1   2   >