]> git.ipfire.org Git - thirdparty/mdadm.git/commitdiff
super-ddf: optimize DDF header search for widely used RAID controllers
authorlilinzhe <llz@antiy.cn>
Mon, 16 Dec 2024 04:11:41 +0000 (12:11 +0800)
committerMariusz Tkaczyk <mtkaczyk@kernel.org>
Mon, 3 Feb 2025 10:19:00 +0000 (11:19 +0100)
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
xmalloc.c
xmalloc.h

index a06ed435bf4676379cbca64bead6b881e43923ad..6e7db924d2b1bcc30b6d90a5c0cb8f3e0032147b 100644 (file)
@@ -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 = {
index e28d3bd6261cf7550a8e4bdda8a2fcc7c750291f..9472005e05d7add3781352ce4031601b1799e3e8 100644 (file)
--- 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();
+}
index 0904b0abc65b4c30035767899752a11be9efa557..789948aea61b3349ee0ba6cd98f6a22dbebf431d 100644 (file)
--- 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