Resolves: RHEL-86676, RHEL-72803, RHEL-88793, RHEL-88791 Signed-off-by: Xiao Ni <xni@redhat.com>
298 lines
7.8 KiB
Diff
298 lines
7.8 KiB
Diff
From f2197b6b6c14af6c788c628acd1fc6d92c268c53 Mon Sep 17 00:00:00 2001
|
|
From: lilinzhe <llz@antiy.cn>
|
|
Date: Mon, 16 Dec 2024 12:11:41 +0800
|
|
Subject: [PATCH 19/37] super-ddf: optimize DDF header search for widely used
|
|
RAID controllers
|
|
|
|
Implemented fallback logic to search the last 32MB of the device
|
|
for the DDF header (magic). If found, proceeds to load the DDF metadata
|
|
from the located position.
|
|
|
|
When clearing metadata as required by the mdadm --zero (function Kill),
|
|
also erase the last 32MB of data; otherwise, it may result in an
|
|
infinite loop.
|
|
|
|
According to the specification, the Anchor Header should be placed at
|
|
the end of the disk. However,some widely used RAID hardware, such as
|
|
LSI and PERC, do not position it within the last 512 bytes of the disk.
|
|
|
|
Signed-off-by: lilinzhe <llz@antiy.cn>
|
|
---
|
|
super-ddf.c | 190 +++++++++++++++++++++++++++++++++++++++++++++++-----
|
|
xmalloc.c | 11 +++
|
|
xmalloc.h | 1 +
|
|
3 files changed, 185 insertions(+), 17 deletions(-)
|
|
|
|
diff --git a/super-ddf.c b/super-ddf.c
|
|
index a06ed435..6e7db924 100644
|
|
--- a/super-ddf.c
|
|
+++ b/super-ddf.c
|
|
@@ -272,6 +272,10 @@ struct phys_disk {
|
|
#define DDF_ReadErrors 32
|
|
#define DDF_Missing 64
|
|
|
|
+
|
|
+#define SEARCH_BLOCK_SIZE 4096
|
|
+#define SEARCH_REGION_SIZE (32 * 1024 * 1024)
|
|
+
|
|
/* The content of the virt_section global scope */
|
|
struct virtual_disk {
|
|
be32 magic; /* DDF_VIRT_RECORDS_MAGIC */
|
|
@@ -877,30 +881,180 @@ static void *load_section(int fd, struct ddf_super *super, void *buf,
|
|
return buf;
|
|
}
|
|
|
|
-static int load_ddf_headers(int fd, struct ddf_super *super, char *devname)
|
|
+
|
|
+/*
|
|
+ * Search for DDF_HEADER_MAGIC in the last 32MB of the device
|
|
+ *
|
|
+ * According to the specification, the Anchor Header should be placed at
|
|
+ * the end of the disk. However,some widely used RAID hardware, such as
|
|
+ * LSI and PERC, do not position it within the last 512 bytes of the disk.
|
|
+ *
|
|
+ */
|
|
+static int search_for_ddf_headers(int fd, char *devname,
|
|
+ unsigned long long *out)
|
|
{
|
|
+ unsigned long long search_start;
|
|
+ unsigned long long search_end;
|
|
+ size_t bytes_block_to_read;
|
|
unsigned long long dsize;
|
|
+ unsigned long long pos;
|
|
+ int bytes_current_read;
|
|
+ size_t offset;
|
|
+
|
|
+ void *buffer = NULL;
|
|
+ be32 *magic_ptr = NULL;
|
|
+
|
|
+ int result = 0;
|
|
|
|
get_dev_size(fd, NULL, &dsize);
|
|
|
|
- if (lseek64(fd, dsize - 512, 0) == -1L) {
|
|
- if (devname)
|
|
- pr_err("Cannot seek to anchor block on %s: %s\n",
|
|
+
|
|
+ /* Determine the search range */
|
|
+ if (dsize > SEARCH_REGION_SIZE)
|
|
+ search_start = dsize - SEARCH_REGION_SIZE;
|
|
+ else
|
|
+ search_start = 0;
|
|
+
|
|
+ search_end = dsize;
|
|
+ pos = search_start;
|
|
+
|
|
+
|
|
+ buffer = xmemalign(SEARCH_BLOCK_SIZE, SEARCH_BLOCK_SIZE);
|
|
+
|
|
+ if (buffer == NULL) {
|
|
+ result = 1;
|
|
+ goto cleanup;
|
|
+ }
|
|
+
|
|
+ while (pos < search_end) {
|
|
+ /* Calculate the number of bytes to read in the current block */
|
|
+ bytes_block_to_read = SEARCH_BLOCK_SIZE;
|
|
+ if (search_end - pos < SEARCH_BLOCK_SIZE)
|
|
+ bytes_block_to_read = search_end - pos;
|
|
+
|
|
+ if (lseek64(fd, pos, SEEK_SET) < 0) {
|
|
+ pr_err("lseek64 for %s failed %d:%s\n",
|
|
+ fd2devnm(fd), errno, strerror(errno));
|
|
+ result = 2;
|
|
+ goto cleanup;
|
|
+ }
|
|
+
|
|
+ /*Read data from the device */
|
|
+ bytes_current_read = read(fd, buffer, bytes_block_to_read);
|
|
+
|
|
+ if (bytes_current_read <= 0) {
|
|
+ pr_err("Failed to read %s. %d:%s, Position=%llu, Bytes to read=%zu. Skipping.\n",
|
|
+ fd2devnm(fd), errno, strerror(errno), pos, bytes_block_to_read);
|
|
+ pos += SEARCH_BLOCK_SIZE; /* Skip to the next block */
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ /* Search for the magic value within the read block */
|
|
+ for (offset = 0;
|
|
+ offset + sizeof(be32) <= (size_t)bytes_current_read;
|
|
+ offset += sizeof(be32)) {
|
|
+
|
|
+ magic_ptr = (be32 *) ((char *)buffer + offset);
|
|
+ if (be32_eq(*magic_ptr, DDF_HEADER_MAGIC)) {
|
|
+ *out = pos + offset;
|
|
+ result = 0;
|
|
+ goto cleanup;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ pos += SEARCH_BLOCK_SIZE;
|
|
+ }
|
|
+
|
|
+cleanup:
|
|
+ free(buffer);
|
|
+ return result;
|
|
+}
|
|
+
|
|
+static int load_ddf_headers(int fd, struct ddf_super *super, char *devname)
|
|
+{
|
|
+ /*
|
|
+ * Load DDF headers from a device.
|
|
+ * First, check at dsize - 512, and if not found, search for it.
|
|
+ */
|
|
+ unsigned long long dsize = 0;
|
|
+ unsigned long long ddfpos = 0;
|
|
+ unsigned long long ddffound = 0;
|
|
+ bool found_anchor = false;
|
|
+
|
|
+ get_dev_size(fd, NULL, &dsize);
|
|
+
|
|
+ /* Check the last 512 bytes for the DDF header. */
|
|
+ if (lseek64(fd, dsize - 512, SEEK_SET) == -1L) {
|
|
+ if (devname) {
|
|
+ pr_err("Cannot seek to last 512 bytes on %s: %s\n",
|
|
devname, strerror(errno));
|
|
+ }
|
|
return 1;
|
|
}
|
|
- if (read(fd, &super->anchor, 512) != 512) {
|
|
- if (devname)
|
|
- pr_err("Cannot read anchor block on %s: %s\n",
|
|
+
|
|
+
|
|
+ /* Read the last 512 bytes into the anchor block */
|
|
+ if (read(fd, &super->anchor, 512) == 512) {
|
|
+ /* Check if the magic value matches */
|
|
+ if (be32_eq(super->anchor.magic, DDF_HEADER_MAGIC))
|
|
+ found_anchor = true;
|
|
+
|
|
+ } else {
|
|
+ if (devname) {
|
|
+ pr_err("Cannot read last 512 bytes on %s: %s\n",
|
|
devname, strerror(errno));
|
|
- return 1;
|
|
+ }
|
|
}
|
|
- if (!be32_eq(super->anchor.magic, DDF_HEADER_MAGIC)) {
|
|
+
|
|
+ if (!found_anchor) {
|
|
+ /* If not found, perform a full search for DDF headers */
|
|
+ ddffound = search_for_ddf_headers(fd, devname, &ddfpos);
|
|
+ if (ddffound != 0) {
|
|
+ if (devname) {
|
|
+ pr_err
|
|
+ ("DDF headers not found during search on %s\n",
|
|
+ devname);
|
|
+ }
|
|
+ return 2;
|
|
+ }
|
|
+
|
|
+ /* Seek to the found position */
|
|
+ if (lseek64(fd, ddfpos, SEEK_SET) == -1L) {
|
|
+ if (devname) {
|
|
+ pr_err("Cannot seek to anchor block on %s\n",
|
|
+ devname);
|
|
+ }
|
|
+ return 1;
|
|
+ }
|
|
+
|
|
+ /* Read the header from the found position */
|
|
+ if (read(fd, &super->anchor, 512) != 512) {
|
|
+ if (devname) {
|
|
+ pr_err
|
|
+ ("Cannot read DDF header at found position on %s: %s\n",
|
|
+ devname, strerror(errno));
|
|
+ }
|
|
+ return 1;
|
|
+ }
|
|
+
|
|
+ /* Verify the magic value again */
|
|
+ if (!be32_eq(super->anchor.magic, DDF_HEADER_MAGIC)) {
|
|
+ if (devname) {
|
|
+ pr_err("Invalid DDF header magic value on %s\n",
|
|
+ devname);
|
|
+ }
|
|
+ return 2;
|
|
+ }
|
|
+ found_anchor = true;
|
|
+ }
|
|
+ if (!found_anchor) {
|
|
+
|
|
if (devname)
|
|
- pr_err("no DDF anchor found on %s\n",
|
|
- devname);
|
|
+ pr_err("DDF headers not found on %s\n", devname);
|
|
+
|
|
return 2;
|
|
}
|
|
+
|
|
if (!be32_eq(calc_crc(&super->anchor, 512), super->anchor.crc)) {
|
|
if (devname)
|
|
pr_err("bad CRC on anchor on %s\n",
|
|
@@ -3889,16 +4043,17 @@ static int store_super_ddf(struct supertype *st, int fd)
|
|
dl->fd = ofd;
|
|
return ret;
|
|
}
|
|
+ // this is used for cleanup (Kill). to clean up 512 bytes
|
|
+ // at the end of the disk is not enough.
|
|
+ // clears SEARCH_REGION_SIZE bytes at the end of the disk.
|
|
|
|
- if (posix_memalign(&buf, 512, 512) != 0)
|
|
- return 1;
|
|
- memset(buf, 0, 512);
|
|
-
|
|
- if (lseek64(fd, dsize - 512, 0) == -1L) {
|
|
+ buf = xmemalign(SEARCH_BLOCK_SIZE, SEARCH_REGION_SIZE);
|
|
+ memset(buf, 0, SEARCH_REGION_SIZE);
|
|
+ if (lseek64(fd, dsize - SEARCH_REGION_SIZE, 0) == -1L) {
|
|
free(buf);
|
|
return 1;
|
|
}
|
|
- rc = write(fd, buf, 512);
|
|
+ rc = write(fd, buf, SEARCH_REGION_SIZE);
|
|
free(buf);
|
|
if (rc < 0)
|
|
return 1;
|
|
@@ -5208,6 +5363,7 @@ static int update_super_ddf_dummy(struct supertype *st, struct mdinfo *info,
|
|
*/
|
|
dprintf("update_super is not implemented in DDF\n");
|
|
return 0;
|
|
+
|
|
}
|
|
|
|
struct superswitch super_ddf = {
|
|
diff --git a/xmalloc.c b/xmalloc.c
|
|
index e28d3bd6..9472005e 100644
|
|
--- a/xmalloc.c
|
|
+++ b/xmalloc.c
|
|
@@ -75,3 +75,14 @@ char *xstrdup(const char *str)
|
|
|
|
return exit_memory_alloc_failure();
|
|
}
|
|
+
|
|
+void *xmemalign(size_t alignment, size_t size)
|
|
+{
|
|
+ void *ptr = NULL;
|
|
+ int result = posix_memalign(&ptr, alignment, size);
|
|
+
|
|
+ if (result == 0)
|
|
+ return ptr;
|
|
+
|
|
+ return exit_memory_alloc_failure();
|
|
+}
|
|
diff --git a/xmalloc.h b/xmalloc.h
|
|
index 0904b0ab..789948ae 100644
|
|
--- a/xmalloc.h
|
|
+++ b/xmalloc.h
|
|
@@ -9,5 +9,6 @@ void *xmalloc(size_t len);
|
|
void *xrealloc(void *ptr, size_t len);
|
|
void *xcalloc(size_t num, size_t size);
|
|
char *xstrdup(const char *str);
|
|
+void *xmemalign(size_t alignment, size_t size);
|
|
|
|
#endif
|
|
--
|
|
2.41.0
|
|
|