]> git.ipfire.org Git - thirdparty/mdadm.git/blobdiff - util.c
manual: update --examine-badblocks
[thirdparty/mdadm.git] / util.c
diff --git a/util.c b/util.c
index d89438c812ef809e18710e052bee584f8525def9..579dd4238a759a092fa58c2928e4bcaf25c6f115 100644 (file)
--- a/util.c
+++ b/util.c
@@ -30,6 +30,7 @@
 #include       <sys/un.h>
 #include       <sys/resource.h>
 #include       <sys/vfs.h>
+#include       <sys/mman.h>
 #include       <linux/magic.h>
 #include       <poll.h>
 #include       <ctype.h>
@@ -127,11 +128,17 @@ static void dlm_ast(void *arg)
 
 static char *cluster_name = NULL;
 /* Create the lockspace, take bitmapXXX locks on all the bitmaps. */
-int cluster_get_dlmlock(int *lockid)
+int cluster_get_dlmlock(void)
 {
        int ret = -1;
        char str[64];
        int flags = LKF_NOQUEUE;
+       int retry_count = 0;
+
+       if (!dlm_funs_ready()) {
+               pr_err("Something wrong with dlm library\n");
+               return -1;
+       }
 
        ret = get_cluster_name(&cluster_name);
        if (ret) {
@@ -140,38 +147,57 @@ int cluster_get_dlmlock(int *lockid)
        }
 
        dlm_lock_res = xmalloc(sizeof(struct dlm_lock_resource));
-       dlm_lock_res->ls = dlm_hooks->create_lockspace(cluster_name, O_RDWR);
+       dlm_lock_res->ls = dlm_hooks->open_lockspace(cluster_name);
        if (!dlm_lock_res->ls) {
-               pr_err("%s failed to create lockspace\n", cluster_name);
-               return -ENOMEM;
+               dlm_lock_res->ls = dlm_hooks->create_lockspace(cluster_name, O_RDWR);
+               if (!dlm_lock_res->ls) {
+                       pr_err("%s failed to create lockspace\n", cluster_name);
+                       return -ENOMEM;
+               }
+       } else {
+               pr_err("open existed %s lockspace\n", cluster_name);
        }
 
        snprintf(str, 64, "bitmap%s", cluster_name);
-       ret = dlm_hooks->ls_lock(dlm_lock_res->ls, LKM_PWMODE, &dlm_lock_res->lksb,
-                         flags, str, strlen(str), 0, dlm_ast,
-                         dlm_lock_res, NULL, NULL);
+retry:
+       ret = dlm_hooks->ls_lock(dlm_lock_res->ls, LKM_PWMODE,
+                                &dlm_lock_res->lksb, flags, str, strlen(str),
+                                0, dlm_ast, dlm_lock_res, NULL, NULL);
        if (ret) {
                pr_err("error %d when get PW mode on lock %s\n", errno, str);
+               /* let's try several times if EAGAIN happened */
+               if (dlm_lock_res->lksb.sb_status == EAGAIN && retry_count < 10) {
+                       sleep(10);
+                       retry_count++;
+                       goto retry;
+               }
                dlm_hooks->release_lockspace(cluster_name, dlm_lock_res->ls, 1);
                return ret;
        }
 
        /* Wait for it to complete */
        poll_for_ast(dlm_lock_res->ls);
-       *lockid = dlm_lock_res->lksb.sb_lkid;
 
-       return dlm_lock_res->lksb.sb_status;
+       if (dlm_lock_res->lksb.sb_status) {
+               pr_err("failed to lock cluster\n");
+               return -1;
+       }
+       return 1;
 }
 
-int cluster_release_dlmlock(int lockid)
+int cluster_release_dlmlock(void)
 {
        int ret = -1;
 
        if (!cluster_name)
-               return -1;
+                goto out;
 
-       ret = dlm_hooks->ls_unlock(dlm_lock_res->ls, lockid, 0,
-                                    &dlm_lock_res->lksb, dlm_lock_res);
+       if (!dlm_lock_res->lksb.sb_lkid)
+                goto out;
+
+       ret = dlm_hooks->ls_unlock_wait(dlm_lock_res->ls,
+                                       dlm_lock_res->lksb.sb_lkid, 0,
+                                       &dlm_lock_res->lksb);
        if (ret) {
                pr_err("error %d happened when unlock\n", errno);
                /* XXX make sure the lock is unlocked eventually */
@@ -183,7 +209,8 @@ int cluster_release_dlmlock(int lockid)
 
        errno = dlm_lock_res->lksb.sb_status;
        if (errno != EUNLOCK) {
-               pr_err("error %d happened in ast when unlock lockspace\n", errno);
+               pr_err("error %d happened in ast when unlock lockspace\n",
+                      errno);
                /* XXX make sure the lockspace is unlocked eventually */
                 goto out;
        }
@@ -228,15 +255,11 @@ int md_array_active(int fd)
 {
        struct mdinfo *sra;
        struct mdu_array_info_s array;
-       int ret;
+       int ret = 0;
 
        sra = sysfs_read(fd, NULL, GET_ARRAY_STATE);
        if (sra) {
-               if (sra->array_state != ARRAY_CLEAR &&
-                   sra->array_state != ARRAY_INACTIVE &&
-                   sra->array_state != ARRAY_UNKNOWN_STATE)
-                       ret = 0;
-               else
+               if (!md_array_is_active(sra))
                        ret = -ENODEV;
 
                free(sra);
@@ -251,6 +274,13 @@ int md_array_active(int fd)
        return !ret;
 }
 
+int md_array_is_active(struct mdinfo *info)
+{
+       return (info->array_state != ARRAY_CLEAR &&
+               info->array_state != ARRAY_INACTIVE &&
+               info->array_state != ARRAY_UNKNOWN_STATE);
+}
+
 /*
  * Get array info from the kernel. Longer term we want to deprecate the
  * ioctl and get it from sysfs.
@@ -276,43 +306,6 @@ int md_get_disk_info(int fd, struct mdu_disk_info_s *disk)
        return ioctl(fd, GET_DISK_INFO, disk);
 }
 
-/*
- * Parse a 128 bit uuid in 4 integers
- * format is 32 hexx nibbles with options :.<space> separator
- * If not exactly 32 hex digits are found, return 0
- * else return 1
- */
-int parse_uuid(char *str, int uuid[4])
-{
-       int hit = 0; /* number of Hex digIT */
-       int i;
-       char c;
-       for (i = 0; i < 4; i++)
-               uuid[i] = 0;
-
-       while ((c = *str++) != 0) {
-               int n;
-               if (c >= '0' && c <= '9')
-                       n = c-'0';
-               else if (c >= 'a' && c <= 'f')
-                       n = 10 + c - 'a';
-               else if (c >= 'A' && c <= 'F')
-                       n = 10 + c - 'A';
-               else if (strchr(":. -", c))
-                       continue;
-               else return 0;
-
-               if (hit<32) {
-                       uuid[hit/8] <<= 4;
-                       uuid[hit/8] += n;
-               }
-               hit++;
-       }
-       if (hit == 32)
-               return 1;
-       return 0;
-}
-
 int get_linux_version()
 {
        struct utsname name;
@@ -359,7 +352,7 @@ int mdadm_version(char *version)
 unsigned long long parse_size(char *size)
 {
        /* parse 'size' which should be a number optionally
-        * followed by 'K', 'M', or 'G'.
+        * followed by 'K', 'M'. 'G' or 'T'.
         * Without a suffix, K is assumed.
         * Number returned is in sectors (half-K)
         * INVALID_SECTORS returned on error.
@@ -381,6 +374,10 @@ unsigned long long parse_size(char *size)
                        c++;
                        s *= 1024 * 1024 * 2;
                        break;
+               case 'T':
+                       c++;
+                       s *= 1024 * 1024 * 1024 * 2LL;
+                       break;
                case 's': /* sectors */
                        c++;
                        break;
@@ -392,6 +389,17 @@ unsigned long long parse_size(char *size)
        return s;
 }
 
+int is_near_layout_10(int layout)
+{
+       int fc, fo;
+
+       fc = (layout >> 8) & 255;
+       fo = layout & (1 << 16);
+       if (fc > 1 || fo > 0)
+               return 0;
+       return 1;
+}
+
 int parse_layout_10(char *layout)
 {
        int copies, rv;
@@ -566,56 +574,6 @@ int enough(int level, int raid_disks, int layout, int clean, char *avail)
        }
 }
 
-const int uuid_zero[4] = { 0, 0, 0, 0 };
-
-int same_uuid(int a[4], int b[4], int swapuuid)
-{
-       if (swapuuid) {
-               /* parse uuids are hostendian.
-                * uuid's from some superblocks are big-ending
-                * if there is a difference, we need to swap..
-                */
-               unsigned char *ac = (unsigned char *)a;
-               unsigned char *bc = (unsigned char *)b;
-               int i;
-               for (i = 0; i < 16; i += 4) {
-                       if (ac[i+0] != bc[i+3] ||
-                           ac[i+1] != bc[i+2] ||
-                           ac[i+2] != bc[i+1] ||
-                           ac[i+3] != bc[i+0])
-                               return 0;
-               }
-               return 1;
-       } else {
-               if (a[0]==b[0] &&
-                   a[1]==b[1] &&
-                   a[2]==b[2] &&
-                   a[3]==b[3])
-                       return 1;
-               return 0;
-       }
-}
-
-void copy_uuid(void *a, int b[4], int swapuuid)
-{
-       if (swapuuid) {
-               /* parse uuids are hostendian.
-                * uuid's from some superblocks are big-ending
-                * if there is a difference, we need to swap..
-                */
-               unsigned char *ac = (unsigned char *)a;
-               unsigned char *bc = (unsigned char *)b;
-               int i;
-               for (i = 0; i < 16; i += 4) {
-                       ac[i+0] = bc[i+3];
-                       ac[i+1] = bc[i+2];
-                       ac[i+2] = bc[i+1];
-                       ac[i+3] = bc[i+0];
-               }
-       } else
-               memcpy(a, b, 16);
-}
-
 char *__fname_from_uuid(int id[4], int swap, char *buf, char sep)
 {
        int i, j;
@@ -636,14 +594,20 @@ char *__fname_from_uuid(int id[4], int swap, char *buf, char sep)
 
 }
 
-char *fname_from_uuid(struct supertype *st, struct mdinfo *info, char *buf, char sep)
+char *fname_from_uuid(struct supertype *st, struct mdinfo *info,
+                     char *buf, char sep)
 {
        // dirty hack to work around an issue with super1 superblocks...
        // super1 superblocks need swapuuid set in order for assembly to
        // work, but can't have it set if we want this printout to match
        // all the other uuid printouts in super1.c, so we force swapuuid
        // to 1 to make our printout match the rest of super1
-       return __fname_from_uuid(info->uuid, (st->ss == &super1) ? 1 : st->ss->swapuuid, buf, sep);
+#if __BYTE_ORDER == BIG_ENDIAN
+       return __fname_from_uuid(info->uuid, 1, buf, sep);
+#else
+       return __fname_from_uuid(info->uuid, (st->ss == &super1) ? 1 :
+                                st->ss->swapuuid, buf, sep);
+#endif
 }
 
 int check_ext2(int fd, char *name)
@@ -846,13 +810,14 @@ char *human_size(long long bytes)
 {
        static char buf[47];
 
-       /* We convert bytes to either centi-M{ega,ibi}bytes or
-        * centi-G{igi,ibi}bytes, with appropriate rounding,
-        * and then print 1/100th of those as a decimal.
+       /* We convert bytes to either centi-M{ega,ibi}bytes,
+        * centi-G{igi,ibi}bytes or centi-T{era,ebi}bytes
+        * with appropriate rounding, and then print
+        * 1/100th of those as a decimal.
         * We allow upto 2048Megabytes before converting to
-        * gigabytes, as that shows more precision and isn't
+        * gigabytes and 2048Gigabytes before converting to
+        * terabytes, as that shows more precision and isn't
         * too large a number.
-        * Terabytes are not yet handled.
         */
 
        if (bytes < 5000*1024)
@@ -862,11 +827,16 @@ char *human_size(long long bytes)
                long cMB  = (bytes / ( 1000000LL / 200LL ) +1) /2;
                snprintf(buf, sizeof(buf), " (%ld.%02ld MiB %ld.%02ld MB)",
                        cMiB/100, cMiB % 100, cMB/100, cMB % 100);
-       } else {
+       } else if (bytes < 2*1024LL*1024LL*1024LL*1024LL) {
                long cGiB = (bytes * 200LL / (1LL<<30) +1) / 2;
                long cGB  = (bytes / (1000000000LL/200LL ) +1) /2;
                snprintf(buf, sizeof(buf), " (%ld.%02ld GiB %ld.%02ld GB)",
                        cGiB/100, cGiB % 100, cGB/100, cGB % 100);
+       } else {
+               long cTiB = (bytes * 200LL / (1LL<<40) + 1) / 2;
+               long cTB  = (bytes / (1000000000000LL / 200LL) + 1) / 2;
+               snprintf(buf, sizeof(buf), " (%ld.%02ld TiB %ld.%02ld TB)",
+                       cTiB/100, cTiB % 100, cTB/100, cTB % 100);
        }
        return buf;
 }
@@ -875,13 +845,14 @@ char *human_size_brief(long long bytes, int prefix)
 {
        static char buf[30];
 
-       /* We convert bytes to either centi-M{ega,ibi}bytes or
-        * centi-G{igi,ibi}bytes, with appropriate rounding,
-        * and then print 1/100th of those as a decimal.
+       /* We convert bytes to either centi-M{ega,ibi}bytes,
+        * centi-G{igi,ibi}bytes or centi-T{era,ebi}bytes
+        * with appropriate rounding, and then print
+        * 1/100th of those as a decimal.
         * We allow upto 2048Megabytes before converting to
-        * gigabytes, as that shows more precision and isn't
+        * gigabytes and 2048Gigabytes before converting to
+        * terabytes, as that shows more precision and isn't
         * too large a number.
-        * Terabytes are not yet handled.
         *
         * If prefix == IEC, we mean prefixes like kibi,mebi,gibi etc.
         * If prefix == JEDEC, we mean prefixes like kilo,mega,giga etc.
@@ -894,10 +865,14 @@ char *human_size_brief(long long bytes, int prefix)
                        long cMiB = (bytes * 200LL / (1LL<<20) +1) /2;
                        snprintf(buf, sizeof(buf), "%ld.%02ldMiB",
                                 cMiB/100, cMiB % 100);
-               } else {
+               } else if (bytes < 2*1024LL*1024LL*1024LL*1024LL) {
                        long cGiB = (bytes * 200LL / (1LL<<30) +1) /2;
                        snprintf(buf, sizeof(buf), "%ld.%02ldGiB",
                                 cGiB/100, cGiB % 100);
+               } else {
+                       long cTiB = (bytes * 200LL / (1LL<<40) + 1) / 2;
+                       snprintf(buf, sizeof(buf), "%ld.%02ldTiB",
+                                cTiB/100, cTiB % 100);
                }
        }
        else if (prefix == JEDEC) {
@@ -905,10 +880,14 @@ char *human_size_brief(long long bytes, int prefix)
                        long cMB  = (bytes / ( 1000000LL / 200LL ) +1) /2;
                        snprintf(buf, sizeof(buf), "%ld.%02ldMB",
                                 cMB/100, cMB % 100);
-               } else {
+               } else if (bytes < 2*1024LL*1024LL*1024LL*1024LL) {
                        long cGB  = (bytes / (1000000000LL/200LL ) +1) /2;
                        snprintf(buf, sizeof(buf), "%ld.%02ldGB",
                                 cGB/100, cGB % 100);
+               } else {
+                       long cTB  = (bytes / (1000000000000LL / 200LL) + 1) / 2;
+                       snprintf(buf, sizeof(buf), "%ld.%02ldTB",
+                                cTB/100, cTB % 100);
                }
        }
        else
@@ -1081,9 +1060,11 @@ int dev_open(char *dev, int flags)
                }
                if (fd < 0) {
                        /* Try /tmp as /dev appear to be read-only */
-                       snprintf(devname, sizeof(devname), "/tmp/.tmp.md.%d:%d:%d",
+                       snprintf(devname, sizeof(devname),
+                                "/tmp/.tmp.md.%d:%d:%d",
                                 (int)getpid(), major, minor);
-                       if (mknod(devname, S_IFBLK|0600, makedev(major, minor)) == 0) {
+                       if (mknod(devname, S_IFBLK|0600,
+                                 makedev(major, minor)) == 0) {
                                fd = open(devname, flags);
                                unlink(devname);
                        }
@@ -1169,7 +1150,7 @@ void wait_for(char *dev, int fd)
                        delay *= 2;
        }
        if (i == 25)
-               dprintf("timeout waiting for %s\n", dev);
+               pr_err("timeout waiting for %s\n", dev);
 }
 
 struct superswitch *superlist[] =
@@ -2104,16 +2085,6 @@ void append_metadata_update(struct supertype *st, void *buf, int len)
 unsigned int __invalid_size_argument_for_IOC = 0;
 #endif
 
-int experimental(void)
-{
-       if (check_env("MDADM_EXPERIMENTAL"))
-               return 1;
-       else {
-               pr_err("To use this feature MDADM_EXPERIMENTAL environment variable has to be defined.\n");
-               return 0;
-       }
-}
-
 /* Pick all spares matching given criteria from a container
  * if min_size == 0 do not check size
  * if domlist == NULL do not check domains
@@ -2258,8 +2229,10 @@ void set_cmap_hooks(void)
        if (!cmap_hooks->cmap_handle)
                return;
 
-       cmap_hooks->initialize = dlsym(cmap_hooks->cmap_handle, "cmap_initialize");
-       cmap_hooks->get_string = dlsym(cmap_hooks->cmap_handle, "cmap_get_string");
+       cmap_hooks->initialize =
+               dlsym(cmap_hooks->cmap_handle, "cmap_initialize");
+       cmap_hooks->get_string =
+               dlsym(cmap_hooks->cmap_handle, "cmap_get_string");
        cmap_hooks->finalize = dlsym(cmap_hooks->cmap_handle, "cmap_finalize");
 
        if (!cmap_hooks->initialize || !cmap_hooks->get_string ||
@@ -2302,16 +2275,22 @@ void set_dlm_hooks(void)
        if (!dlm_hooks->dlm_handle)
                return;
 
-       dlm_hooks->create_lockspace = dlsym(dlm_hooks->dlm_handle, "dlm_create_lockspace");
-       dlm_hooks->release_lockspace = dlsym(dlm_hooks->dlm_handle, "dlm_release_lockspace");
+       dlm_hooks->open_lockspace =
+               dlsym(dlm_hooks->dlm_handle, "dlm_open_lockspace");
+       dlm_hooks->create_lockspace =
+               dlsym(dlm_hooks->dlm_handle, "dlm_create_lockspace");
+       dlm_hooks->release_lockspace =
+               dlsym(dlm_hooks->dlm_handle, "dlm_release_lockspace");
        dlm_hooks->ls_lock = dlsym(dlm_hooks->dlm_handle, "dlm_ls_lock");
-       dlm_hooks->ls_unlock = dlsym(dlm_hooks->dlm_handle, "dlm_ls_unlock");
+       dlm_hooks->ls_unlock_wait =
+               dlsym(dlm_hooks->dlm_handle, "dlm_ls_unlock_wait");
        dlm_hooks->ls_get_fd = dlsym(dlm_hooks->dlm_handle, "dlm_ls_get_fd");
        dlm_hooks->dispatch = dlsym(dlm_hooks->dlm_handle, "dlm_dispatch");
 
-       if (!dlm_hooks->create_lockspace || !dlm_hooks->ls_lock ||
-           !dlm_hooks->ls_unlock || !dlm_hooks->release_lockspace ||
-           !dlm_hooks->ls_get_fd || !dlm_hooks->dispatch)
+       if (!dlm_hooks->open_lockspace || !dlm_hooks->create_lockspace ||
+           !dlm_hooks->ls_lock || !dlm_hooks->ls_unlock_wait ||
+           !dlm_hooks->release_lockspace || !dlm_hooks->ls_get_fd ||
+           !dlm_hooks->dispatch)
                dlclose(dlm_hooks->dlm_handle);
        else
                is_dlm_hooks_ready = 1;
@@ -2322,3 +2301,51 @@ void set_hooks(void)
        set_dlm_hooks();
        set_cmap_hooks();
 }
+
+int zero_disk_range(int fd, unsigned long long sector, size_t count)
+{
+       int ret = 0;
+       int fd_zero;
+       void *addr = NULL;
+       size_t written = 0;
+       size_t len = count * 512;
+       ssize_t n;
+
+       fd_zero = open("/dev/zero", O_RDONLY);
+       if (fd_zero < 0) {
+               pr_err("Cannot open /dev/zero\n");
+               return -1;
+       }
+
+       if (lseek64(fd, sector * 512, SEEK_SET) < 0) {
+               ret = -errno;
+               pr_err("Failed to seek offset for zeroing\n");
+               goto out;
+       }
+
+       addr = mmap(NULL, len, PROT_READ, MAP_PRIVATE, fd_zero, 0);
+
+       if (addr == MAP_FAILED) {
+               ret = -errno;
+               pr_err("Mapping /dev/zero failed\n");
+               goto out;
+       }
+
+       do {
+               n = write(fd, addr + written, len - written);
+               if (n < 0) {
+                       if (errno == EINTR)
+                               continue;
+                       ret = -errno;
+                       pr_err("Zeroing disk range failed\n");
+                       break;
+               }
+               written += n;
+       } while (written != len);
+
+       munmap(addr, len);
+
+out:
+       close(fd_zero);
+       return ret;
+}