]> git.ipfire.org Git - thirdparty/mdadm.git/blobdiff - sysfs.c
sysfs/sysfs_read: Count active_disks and failed_disks
[thirdparty/mdadm.git] / sysfs.c
diff --git a/sysfs.c b/sysfs.c
index 19d56aa939afc5f58a8104ebcc1bde2e3acc27ad..f7967e88489ae45a9306b5aa1fa7c3a94b8e476c 100644 (file)
--- a/sysfs.c
+++ b/sysfs.c
 #include       <dirent.h>
 #include       <ctype.h>
 
-int load_sys(char *path, char *buf)
+#define MAX_SYSFS_PATH_LEN     120
+
+int load_sys(char *path, char *buf, int len)
 {
        int fd = open(path, O_RDONLY);
        int n;
        if (fd < 0)
                return -1;
-       n = read(fd, buf, 1024);
+       n = read(fd, buf, len);
        close(fd);
-       if (n <0 || n >= 1024)
+       if (n <0 || n >= len)
                return -1;
        buf[n] = 0;
        if (n && buf[n-1] == '\n')
@@ -50,8 +52,10 @@ void sysfs_free(struct mdinfo *sra)
                while (sra->devs) {
                        struct mdinfo *d = sra->devs;
                        sra->devs = d->next;
+                       free(d->bb.entries);
                        free(d);
                }
+               free(sra->bb.entries);
                free(sra);
                sra = sra2;
        }
@@ -59,33 +63,51 @@ void sysfs_free(struct mdinfo *sra)
 
 int sysfs_open(char *devnm, char *devname, char *attr)
 {
-       char fname[50];
+       char fname[MAX_SYSFS_PATH_LEN];
        int fd;
 
-       sprintf(fname, "/sys/block/%s/md/", devnm);
+       snprintf(fname, MAX_SYSFS_PATH_LEN, "/sys/block/%s/md/", devnm);
        if (devname) {
-               strcat(fname, devname);
-               strcat(fname, "/");
+               strncat(fname, devname, MAX_SYSFS_PATH_LEN - strlen(fname));
+               strncat(fname, "/", MAX_SYSFS_PATH_LEN - strlen(fname));
        }
-       strcat(fname, attr);
+       strncat(fname, attr, MAX_SYSFS_PATH_LEN - strlen(fname));
        fd = open(fname, O_RDWR);
        if (fd < 0 && errno == EACCES)
                fd = open(fname, O_RDONLY);
        return fd;
 }
 
-void sysfs_init(struct mdinfo *mdi, int fd, char *devnm)
+void sysfs_init_dev(struct mdinfo *mdi, unsigned long devid)
 {
+       snprintf(mdi->sys_name,
+                sizeof(mdi->sys_name), "dev-%s", devid2kname(devid));
+}
+
+int sysfs_init(struct mdinfo *mdi, int fd, char *devnm)
+{
+       struct stat stb;
+       char fname[MAX_SYSFS_PATH_LEN];
+       int retval = -ENODEV;
+
        mdi->sys_name[0] = 0;
-       if (fd >= 0) {
-               mdu_version_t vers;
-               if (ioctl(fd, RAID_VERSION, &vers) != 0)
-                       return;
+       if (fd >= 0)
                devnm = fd2devnm(fd);
-       }
+
        if (devnm == NULL)
-               return;
+               goto out;
+
+       snprintf(fname, MAX_SYSFS_PATH_LEN, "/sys/block/%s/md", devnm);
+
+       if (stat(fname, &stb))
+               goto out;
+       if (!S_ISDIR(stb.st_mode))
+               goto out;
        strcpy(mdi->sys_name, devnm);
+
+       retval = 0;
+out:
+       return retval;
 }
 
 struct mdinfo *sysfs_read(int fd, char *devnm, unsigned long options)
@@ -100,8 +122,7 @@ struct mdinfo *sysfs_read(int fd, char *devnm, unsigned long options)
        struct dirent *de;
 
        sra = xcalloc(1, sizeof(*sra));
-       sysfs_init(sra, fd, devnm);
-       if (sra->sys_name[0] == 0) {
+       if (sysfs_init(sra, fd, devnm)) {
                free(sra);
                return NULL;
        }
@@ -112,7 +133,7 @@ struct mdinfo *sysfs_read(int fd, char *devnm, unsigned long options)
        sra->devs = NULL;
        if (options & GET_VERSION) {
                strcpy(base, "metadata_version");
-               if (load_sys(fname, buf))
+               if (load_sys(fname, buf, sizeof(buf)))
                        goto abort;
                if (strncmp(buf, "none", 4) == 0) {
                        sra->array.major_version =
@@ -131,31 +152,31 @@ struct mdinfo *sysfs_read(int fd, char *devnm, unsigned long options)
        }
        if (options & GET_LEVEL) {
                strcpy(base, "level");
-               if (load_sys(fname, buf))
+               if (load_sys(fname, buf, sizeof(buf)))
                        goto abort;
                sra->array.level = map_name(pers, buf);
        }
        if (options & GET_LAYOUT) {
                strcpy(base, "layout");
-               if (load_sys(fname, buf))
+               if (load_sys(fname, buf, sizeof(buf)))
                        goto abort;
                sra->array.layout = strtoul(buf, NULL, 0);
        }
        if (options & GET_DISKS) {
                strcpy(base, "raid_disks");
-               if (load_sys(fname, buf))
+               if (load_sys(fname, buf, sizeof(buf)))
                        goto abort;
                sra->array.raid_disks = strtoul(buf, NULL, 0);
        }
        if (options & GET_DEGRADED) {
                strcpy(base, "degraded");
-               if (load_sys(fname, buf))
+               if (load_sys(fname, buf, sizeof(buf)))
                        goto abort;
                sra->array.failed_disks = strtoul(buf, NULL, 0);
        }
        if (options & GET_COMPONENT) {
                strcpy(base, "component_size");
-               if (load_sys(fname, buf))
+               if (load_sys(fname, buf, sizeof(buf)))
                        goto abort;
                sra->component_size = strtoull(buf, NULL, 0);
                /* sysfs reports "K", but we want sectors */
@@ -163,13 +184,13 @@ struct mdinfo *sysfs_read(int fd, char *devnm, unsigned long options)
        }
        if (options & GET_CHUNK) {
                strcpy(base, "chunk_size");
-               if (load_sys(fname, buf))
+               if (load_sys(fname, buf, sizeof(buf)))
                        goto abort;
                sra->array.chunk_size = strtoul(buf, NULL, 0);
        }
        if (options & GET_CACHE) {
                strcpy(base, "stripe_cache_size");
-               if (load_sys(fname, buf))
+               if (load_sys(fname, buf, sizeof(buf)))
                        /* Probably level doesn't support it */
                        sra->cache_size = 0;
                else
@@ -177,7 +198,7 @@ struct mdinfo *sysfs_read(int fd, char *devnm, unsigned long options)
        }
        if (options & GET_MISMATCH) {
                strcpy(base, "mismatch_cnt");
-               if (load_sys(fname, buf))
+               if (load_sys(fname, buf, sizeof(buf)))
                        goto abort;
                sra->mismatch_cnt = strtoul(buf, NULL, 0);
        }
@@ -189,7 +210,7 @@ struct mdinfo *sysfs_read(int fd, char *devnm, unsigned long options)
                size_t len;
 
                strcpy(base, "safe_mode_delay");
-               if (load_sys(fname, buf))
+               if (load_sys(fname, buf, sizeof(buf)))
                        goto abort;
 
                /* remove a period, and count digits after it */
@@ -212,7 +233,7 @@ struct mdinfo *sysfs_read(int fd, char *devnm, unsigned long options)
        }
        if (options & GET_BITMAP_LOCATION) {
                strcpy(base, "bitmap/location");
-               if (load_sys(fname, buf))
+               if (load_sys(fname, buf, sizeof(buf)))
                        goto abort;
                if (strncmp(buf, "file", 4) == 0)
                        sra->bitmap_offset = 1;
@@ -224,6 +245,22 @@ struct mdinfo *sysfs_read(int fd, char *devnm, unsigned long options)
                        goto abort;
        }
 
+       if (options & GET_ARRAY_STATE) {
+               strcpy(base, "array_state");
+               if (load_sys(fname, buf, sizeof(buf)))
+                       goto abort;
+               sra->array_state = map_name(sysfs_array_states, buf);
+       }
+
+       if (options & GET_CONSISTENCY_POLICY) {
+               strcpy(base, "consistency_policy");
+               if (load_sys(fname, buf, sizeof(buf)))
+                       sra->consistency_policy = CONSISTENCY_POLICY_UNKNOWN;
+               else
+                       sra->consistency_policy = map_name(consistency_policies,
+                                                          buf);
+       }
+
        if (! (options & GET_DEVS))
                return sra;
 
@@ -233,6 +270,8 @@ struct mdinfo *sysfs_read(int fd, char *devnm, unsigned long options)
        if (!dir)
                goto abort;
        sra->array.spare_disks = 0;
+       sra->array.active_disks = 0;
+       sra->array.failed_disks = 0;
 
        devp = &sra->devs;
        sra->devs = NULL;
@@ -245,11 +284,11 @@ struct mdinfo *sysfs_read(int fd, char *devnm, unsigned long options)
                dbase = base + strlen(base);
                *dbase++ = '/';
 
-               dev = xmalloc(sizeof(*dev));
+               dev = xcalloc(1, sizeof(*dev));
 
                /* Always get slot, major, minor */
                strcpy(dbase, "slot");
-               if (load_sys(fname, buf)) {
+               if (load_sys(fname, buf, sizeof(buf))) {
                        /* hmm... unable to read 'slot' maybe the device
                         * is going away?
                         */
@@ -274,7 +313,7 @@ struct mdinfo *sysfs_read(int fd, char *devnm, unsigned long options)
                if (*ep) dev->disk.raid_disk = -1;
 
                strcpy(dbase, "block/dev");
-               if (load_sys(fname, buf)) {
+               if (load_sys(fname, buf, sizeof(buf))) {
                        /* assume this is a stale reference to a hot
                         * removed device
                         */
@@ -286,7 +325,7 @@ struct mdinfo *sysfs_read(int fd, char *devnm, unsigned long options)
 
                /* special case check for block devices that can go 'offline' */
                strcpy(dbase, "block/device/state");
-               if (load_sys(fname, buf) == 0 &&
+               if (load_sys(fname, buf, sizeof(buf)) == 0 &&
                    strncmp(buf, "offline", 7) == 0) {
                        free(dev);
                        continue;
@@ -299,36 +338,40 @@ struct mdinfo *sysfs_read(int fd, char *devnm, unsigned long options)
 
                if (options & GET_OFFSET) {
                        strcpy(dbase, "offset");
-                       if (load_sys(fname, buf))
+                       if (load_sys(fname, buf, sizeof(buf)))
                                goto abort;
                        dev->data_offset = strtoull(buf, NULL, 0);
                        strcpy(dbase, "new_offset");
-                       if (load_sys(fname, buf) == 0)
+                       if (load_sys(fname, buf, sizeof(buf)) == 0)
                                dev->new_data_offset = strtoull(buf, NULL, 0);
                        else
                                dev->new_data_offset = dev->data_offset;
                }
                if (options & GET_SIZE) {
                        strcpy(dbase, "size");
-                       if (load_sys(fname, buf))
+                       if (load_sys(fname, buf, sizeof(buf)))
                                goto abort;
                        dev->component_size = strtoull(buf, NULL, 0) * 2;
                }
                if (options & GET_STATE) {
                        dev->disk.state = 0;
                        strcpy(dbase, "state");
-                       if (load_sys(fname, buf))
+                       if (load_sys(fname, buf, sizeof(buf)))
                                goto abort;
-                       if (strstr(buf, "in_sync"))
+                       if (strstr(buf, "in_sync")) {
                                dev->disk.state |= (1<<MD_DISK_SYNC);
-                       if (strstr(buf, "faulty"))
+                               sra->array.active_disks++;
+                       }
+                       if (strstr(buf, "faulty")) {
                                dev->disk.state |= (1<<MD_DISK_FAULTY);
+                               sra->array.failed_disks++;
+                       }
                        if (dev->disk.state == 0)
                                sra->array.spare_disks++;
                }
                if (options & GET_ERROR) {
                        strcpy(buf, "errors");
-                       if (load_sys(fname, buf))
+                       if (load_sys(fname, buf, sizeof(buf)))
                                goto abort;
                        dev->errors = strtoul(buf, NULL, 0);
                }
@@ -378,15 +421,12 @@ unsigned long long get_component_size(int fd)
         * This returns in units of sectors.
         */
        struct stat stb;
-       char fname[50];
+       char fname[MAX_SYSFS_PATH_LEN];
        int n;
-       if (fstat(fd, &stb)) return 0;
-       if (major(stb.st_rdev) != (unsigned)get_mdp_major())
-               sprintf(fname, "/sys/block/md%d/md/component_size",
-                       (int)minor(stb.st_rdev));
-       else
-               sprintf(fname, "/sys/block/md_d%d/md/component_size",
-                       (int)minor(stb.st_rdev)>>MdpMinorShift);
+       if (fstat(fd, &stb))
+               return 0;
+       snprintf(fname, MAX_SYSFS_PATH_LEN,
+                "/sys/block/%s/md/component_size", stat2devnm(&stb));
        fd = open(fname, O_RDONLY);
        if (fd < 0)
                return 0;
@@ -401,11 +441,11 @@ unsigned long long get_component_size(int fd)
 int sysfs_set_str(struct mdinfo *sra, struct mdinfo *dev,
                  char *name, char *val)
 {
-       char fname[50];
+       char fname[MAX_SYSFS_PATH_LEN];
        unsigned int n;
        int fd;
 
-       sprintf(fname, "/sys/block/%s/md/%s/%s",
+       snprintf(fname, MAX_SYSFS_PATH_LEN, "/sys/block/%s/md/%s/%s",
                sra->sys_name, dev?dev->sys_name:"", name);
        fd = open(fname, O_WRONLY);
        if (fd < 0)
@@ -413,7 +453,7 @@ int sysfs_set_str(struct mdinfo *sra, struct mdinfo *dev,
        n = write(fd, val, strlen(val));
        close(fd);
        if (n != strlen(val)) {
-               dprintf(Name ": failed to write '%s' to '%s' (%s)\n",
+               dprintf("failed to write '%s' to '%s' (%s)\n",
                        val, fname, strerror(errno));
                return -1;
        }
@@ -438,11 +478,11 @@ int sysfs_set_num_signed(struct mdinfo *sra, struct mdinfo *dev,
 
 int sysfs_uevent(struct mdinfo *sra, char *event)
 {
-       char fname[50];
+       char fname[MAX_SYSFS_PATH_LEN];
        int n;
        int fd;
 
-       sprintf(fname, "/sys/block/%s/uevent",
+       snprintf(fname, MAX_SYSFS_PATH_LEN, "/sys/block/%s/uevent",
                sra->sys_name);
        fd = open(fname, O_WRONLY);
        if (fd < 0)
@@ -450,7 +490,7 @@ int sysfs_uevent(struct mdinfo *sra, char *event)
        n = write(fd, event, strlen(event));
        close(fd);
        if (n != (int)strlen(event)) {
-               dprintf(Name ": failed to write '%s' to '%s' (%s)\n",
+               dprintf("failed to write '%s' to '%s' (%s)\n",
                        event, fname, strerror(errno));
                return -1;
        }
@@ -459,10 +499,10 @@ int sysfs_uevent(struct mdinfo *sra, char *event)
 
 int sysfs_attribute_available(struct mdinfo *sra, struct mdinfo *dev, char *name)
 {
-       char fname[50];
+       char fname[MAX_SYSFS_PATH_LEN];
        struct stat st;
 
-       sprintf(fname, "/sys/block/%s/md/%s/%s",
+       snprintf(fname, MAX_SYSFS_PATH_LEN, "/sys/block/%s/md/%s/%s",
                sra->sys_name, dev?dev->sys_name:"", name);
 
        return stat(fname, &st) == 0;
@@ -471,10 +511,10 @@ int sysfs_attribute_available(struct mdinfo *sra, struct mdinfo *dev, char *name
 int sysfs_get_fd(struct mdinfo *sra, struct mdinfo *dev,
                       char *name)
 {
-       char fname[50];
+       char fname[MAX_SYSFS_PATH_LEN];
        int fd;
 
-       sprintf(fname, "/sys/block/%s/md/%s/%s",
+       snprintf(fname, MAX_SYSFS_PATH_LEN, "/sys/block/%s/md/%s/%s",
                sra->sys_name, dev?dev->sys_name:"", name);
        fd = open(fname, O_RDWR);
        if (fd < 0)
@@ -490,7 +530,7 @@ int sysfs_fd_get_ll(int fd, unsigned long long *val)
 
        lseek(fd, 0, 0);
        n = read(fd, buf, sizeof(buf));
-       if (n <= 0)
+       if (n <= 0 || n == sizeof(buf))
                return -2;
        buf[n] = 0;
        *val = strtoull(buf, &ep, 0);
@@ -526,7 +566,7 @@ int sysfs_fd_get_two(int fd, unsigned long long *v1, unsigned long long *v2)
 
        lseek(fd, 0, 0);
        n = read(fd, buf, sizeof(buf));
-       if (n <= 0)
+       if (n <= 0 || n == sizeof(buf))
                return -2;
        buf[n] = 0;
        *v1 = strtoull(buf, &ep, 0);
@@ -562,7 +602,7 @@ int sysfs_fd_get_str(int fd, char *val, int size)
 
        lseek(fd, 0, 0);
        n = read(fd, val, size);
-       if (n <= 0)
+       if (n <= 0 || n == size)
                return -1;
        val[n] = 0;
        return n;
@@ -623,8 +663,7 @@ int sysfs_set_array(struct mdinfo *info, int vers)
                if ((vers % 100) < 2 ||
                    sysfs_set_str(info, NULL, "metadata_version",
                                  ver) < 0) {
-                       pr_err("This kernel does not "
-                               "support external metadata.\n");
+                       pr_err("This kernel does not support external metadata.\n");
                        return 1;
                }
        }
@@ -644,9 +683,7 @@ int sysfs_set_array(struct mdinfo *info, int vers)
                rc = sysfs_set_num(info, NULL, "array_size",
                                   info->custom_array_size/2);
                if (rc && errno == ENOENT) {
-                       pr_err("This kernel does not "
-                               "have the md/array_size attribute, "
-                               "the array may be larger than expected\n");
+                       pr_err("This kernel does not have the md/array_size attribute, the array may be larger than expected\n");
                        rc = 0;
                }
                rv |= rc;
@@ -666,6 +703,16 @@ int sysfs_set_array(struct mdinfo *info, int vers)
                 * once the reshape completes.
                 */
        }
+
+       if (info->consistency_policy == CONSISTENCY_POLICY_PPL) {
+               if (sysfs_set_str(info, NULL, "consistency_policy",
+                                 map_num(consistency_policies,
+                                         info->consistency_policy))) {
+                       pr_err("This kernel does not support PPL\n");
+                       return 1;
+               }
+       }
+
        return rv;
 }
 
@@ -675,6 +722,7 @@ int sysfs_add_disk(struct mdinfo *sra, struct mdinfo *sd, int resume)
        char nm[PATH_MAX];
        char *dname;
        int rv;
+       int i;
 
        sprintf(dv, "%d:%d", sd->disk.major, sd->disk.minor);
        rv = sysfs_set_str(sra, NULL, "new_dev", dv);
@@ -682,7 +730,7 @@ int sysfs_add_disk(struct mdinfo *sra, struct mdinfo *sd, int resume)
                return rv;
 
        memset(nm, 0, sizeof(nm));
-       dname = devid2devnm(makedev(sd->disk.major, sd->disk.minor));
+       dname = devid2kname(makedev(sd->disk.major, sd->disk.minor));
        strcpy(sd->sys_name, "dev-");
        strcpy(sd->sys_name+4, dname);
 
@@ -696,6 +744,10 @@ int sysfs_add_disk(struct mdinfo *sra, struct mdinfo *sd, int resume)
        rv = sysfs_set_num(sra, sd, "offset", sd->data_offset);
        rv |= sysfs_set_num(sra, sd, "size", (sd->component_size+1) / 2);
        if (sra->array.level != LEVEL_CONTAINER) {
+               if (sd->consistency_policy == CONSISTENCY_POLICY_PPL) {
+                       rv |= sysfs_set_num(sra, sd, "ppl_sector", sd->ppl_sector);
+                       rv |= sysfs_set_num(sra, sd, "ppl_size", sd->ppl_size);
+               }
                if (sd->recovery_start == MaxSector)
                        /* This can correctly fail if array isn't started,
                         * yet, so just ignore status for now.
@@ -706,6 +758,28 @@ int sysfs_add_disk(struct mdinfo *sra, struct mdinfo *sd, int resume)
                if (resume)
                        sysfs_set_num(sra, sd, "recovery_start", sd->recovery_start);
        }
+       if (sd->bb.supported) {
+               if (sysfs_set_str(sra, sd, "state", "external_bbl")) {
+                       /*
+                        * backward compatibility - if kernel doesn't support
+                        * bad blocks for external metadata, let it continue
+                        * as long as there are none known so far
+                        */
+                       if (sd->bb.count) {
+                               pr_err("The kernel has no support for bad blocks in external metadata\n");
+                               return -1;
+                       }
+               }
+
+               for (i = 0; i < sd->bb.count; i++) {
+                       char s[30];
+                       const struct md_bb_entry *entry = &sd->bb.entries[i];
+
+                       snprintf(s, sizeof(s) - 1, "%llu %d\n", entry->sector,
+                                entry->length);
+                       rv |= sysfs_set_str(sra, sd, "bad_blocks", s);
+               }
+       }
        return rv;
 }
 
@@ -718,7 +792,7 @@ int sysfs_disk_to_sg(int fd)
        struct stat st;
        char path[256];
        char sg_path[256];
-       char sg_major_minor[8];
+       char sg_major_minor[10];
        char *c;
        DIR *dir;
        struct dirent *de;
@@ -753,7 +827,7 @@ int sysfs_disk_to_sg(int fd)
 
        rv = read(fd, sg_major_minor, sizeof(sg_major_minor));
        close(fd);
-       if (rv < 0)
+       if (rv < 0 || rv == sizeof(sg_major_minor))
                return -1;
        else
                sg_major_minor[rv - 1] = '\0';
@@ -904,15 +978,18 @@ int sysfs_wait(int fd, int *msec)
        else {
                struct timeval start, end, tv;
                gettimeofday(&start, NULL);
-               if (*msec < 1000)
+               if (*msec < 1000) {
+                       tv.tv_sec = 0;
                        tv.tv_usec = (*msec)*1000;
-               else
+               } else {
                        tv.tv_sec = (*msec)/1000;
+                       tv.tv_usec = 0;
+               }
                n = select(fd+1, NULL, NULL, &fds, &tv);
                gettimeofday(&end, NULL);
                end.tv_sec -= start.tv_sec;
                *msec -= (end.tv_sec * 1000 + end.tv_usec/1000
-                         - start.tv_usec/100) + 1;
+                         - start.tv_usec/1000) + 1;
        }
        return n;
 }