X-Git-Url: http://git.ipfire.org/?p=thirdparty%2Fmdadm.git;a=blobdiff_plain;f=sysfs.c;h=93ec3de826c1c491c9f92a6255667683b025c66d;hp=f0773d476611fa8e66682ed770d0141de22e4263;hb=67a02d520085b01a1b9e6ea59fb30e79c5649c9c;hpb=f897078e8b250d1534a31e111e3168e82bfe532b diff --git a/sysfs.c b/sysfs.c index f0773d47..93ec3de8 100644 --- a/sysfs.c +++ b/sysfs.c @@ -27,15 +27,17 @@ #include #include -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,66 +52,72 @@ 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; } } -int sysfs_open(int devnum, char *devname, char *attr) +int sysfs_open(char *devnm, char *devname, char *attr) { - char fname[50]; + char fname[MAX_SYSFS_PATH_LEN]; int fd; - char *mdname = devnum2devname(devnum); - - if (!mdname) - return -1; - sprintf(fname, "/sys/block/%s/md/", mdname); + 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); - free(mdname); return fd; } -void sysfs_init(struct mdinfo *mdi, int fd, int devnum) +void sysfs_init_dev(struct mdinfo *mdi, unsigned long devid) +{ + snprintf(mdi->sys_name, + sizeof(mdi->sys_name), "dev-%s", devid2kname(devid)); +} + +void sysfs_init(struct mdinfo *mdi, int fd, char *devnm) { + struct stat stb; + char fname[MAX_SYSFS_PATH_LEN]; + mdi->sys_name[0] = 0; - if (fd >= 0) { - mdu_version_t vers; - if (ioctl(fd, RAID_VERSION, &vers) != 0) - return; - devnum = fd2devnum(fd); - } - if (devnum == NoMdDev) + if (fd >= 0) + devnm = fd2devnm(fd); + + if (devnm == NULL) return; - fmt_devname(mdi->sys_name, devnum); -} + snprintf(fname, MAX_SYSFS_PATH_LEN, "/sys/block/%s/md", devnm); + + if (stat(fname, &stb)) + return; + if (!S_ISDIR(stb.st_mode)) + return; + strcpy(mdi->sys_name, devnm); +} -struct mdinfo *sysfs_read(int fd, int devnum, unsigned long options) +struct mdinfo *sysfs_read(int fd, char *devnm, unsigned long options) { char fname[PATH_MAX]; char buf[PATH_MAX]; char *base; char *dbase; struct mdinfo *sra; - struct mdinfo *dev; + struct mdinfo *dev, **devp; DIR *dir = NULL; struct dirent *de; - sra = malloc(sizeof(*sra)); - if (sra == NULL) - return sra; - memset(sra, 0, sizeof(*sra)); - sysfs_init(sra, fd, devnum); + sra = xcalloc(1, sizeof(*sra)); + sysfs_init(sra, fd, devnm); if (sra->sys_name[0] == 0) { free(sra); return NULL; @@ -121,7 +129,7 @@ struct mdinfo *sysfs_read(int fd, int devnum, 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 = @@ -140,31 +148,31 @@ struct mdinfo *sysfs_read(int fd, int devnum, 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 */ @@ -172,19 +180,21 @@ struct mdinfo *sysfs_read(int fd, int devnum, 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)) - goto abort; - sra->cache_size = strtoul(buf, NULL, 0); + if (load_sys(fname, buf, sizeof(buf))) + /* Probably level doesn't support it */ + sra->cache_size = 0; + else + sra->cache_size = strtoul(buf, NULL, 0); } 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); } @@ -196,7 +206,7 @@ struct mdinfo *sysfs_read(int fd, int devnum, 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 */ @@ -217,6 +227,38 @@ struct mdinfo *sysfs_read(int fd, int devnum, unsigned long options) msec = (msec * 1000) / scale; sra->safe_mode_delay = msec; } + if (options & GET_BITMAP_LOCATION) { + strcpy(base, "bitmap/location"); + if (load_sys(fname, buf, sizeof(buf))) + goto abort; + if (strncmp(buf, "file", 4) == 0) + sra->bitmap_offset = 1; + else if (strncmp(buf, "none", 4) == 0) + sra->bitmap_offset = 0; + else if (buf[0] == '+') + sra->bitmap_offset = strtol(buf+1, NULL, 10); + else + goto abort; + } + + if (options & GET_ARRAY_STATE) { + strcpy(base, "array_state"); + if (load_sys(fname, sra->sysfs_array_state, + sizeof(sra->sysfs_array_state))) + goto abort; + } else + sra->sysfs_array_state[0] = 0; + + 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 (sra->consistency_policy == UnSet) + sra->consistency_policy = CONSISTENCY_POLICY_UNKNOWN; + } + } if (! (options & GET_DEVS)) return sra; @@ -228,6 +270,8 @@ struct mdinfo *sysfs_read(int fd, int devnum, unsigned long options) goto abort; sra->array.spare_disks = 0; + devp = &sra->devs; + sra->devs = NULL; while ((de = readdir(dir)) != NULL) { char *ep; if (de->d_ino == 0 || @@ -237,13 +281,11 @@ struct mdinfo *sysfs_read(int fd, int devnum, unsigned long options) dbase = base + strlen(base); *dbase++ = '/'; - dev = malloc(sizeof(*dev)); - if (!dev) - goto abort; + 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? */ @@ -261,50 +303,57 @@ struct mdinfo *sysfs_read(int fd, int devnum, unsigned long options) free(dev); goto abort; } - + } strcpy(dev->sys_name, de->d_name); dev->disk.raid_disk = strtoul(buf, &ep, 10); 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 */ free(dev); continue; } + sra->array.nr_disks++; sscanf(buf, "%d:%d", &dev->disk.major, &dev->disk.minor); /* 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; } /* finally add this disk to the array */ - dev->next = sra->devs; - sra->devs = dev; + *devp = dev; + devp = & dev->next; + dev->next = NULL; 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, 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")) dev->disk.state |= (1<errors = strtoul(buf, NULL, 0); } @@ -365,21 +414,18 @@ 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; n = read(fd, fname, sizeof(fname)); close(fd); - if (n == sizeof(fname)) + if (n < 0 || n == sizeof(fname)) return 0; fname[n] = 0; return strtoull(fname, NULL, 10) * 2; @@ -388,11 +434,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) @@ -400,7 +446,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; } @@ -415,28 +461,41 @@ int sysfs_set_num(struct mdinfo *sra, struct mdinfo *dev, return sysfs_set_str(sra, dev, name, valstr); } +int sysfs_set_num_signed(struct mdinfo *sra, struct mdinfo *dev, + char *name, long long val) +{ + char valstr[50]; + sprintf(valstr, "%lli", val); + return sysfs_set_str(sra, dev, name, valstr); +} + 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) return -1; n = write(fd, event, strlen(event)); close(fd); + if (n != (int)strlen(event)) { + dprintf("failed to write '%s' to '%s' (%s)\n", + event, fname, strerror(errno)); + return -1; + } return 0; -} +} 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; @@ -445,10 +504,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) @@ -464,8 +523,8 @@ int sysfs_fd_get_ll(int fd, unsigned long long *val) lseek(fd, 0, 0); n = read(fd, buf, sizeof(buf)); - if (n <= 0) - return -1; + if (n <= 0 || n == sizeof(buf)) + return -2; buf[n] = 0; *val = strtoull(buf, &ep, 0); if (ep == buf || (*ep != 0 && *ep != '\n' && *ep != ' ')) @@ -487,13 +546,56 @@ int sysfs_get_ll(struct mdinfo *sra, struct mdinfo *dev, return n; } +int sysfs_fd_get_two(int fd, unsigned long long *v1, unsigned long long *v2) +{ + /* two numbers in this sysfs file, either + * NNN (NNN) + * or + * NNN / NNN + */ + char buf[80]; + int n; + char *ep, *ep2; + + lseek(fd, 0, 0); + n = read(fd, buf, sizeof(buf)); + if (n <= 0 || n == sizeof(buf)) + return -2; + buf[n] = 0; + *v1 = strtoull(buf, &ep, 0); + if (ep == buf || (*ep != 0 && *ep != '\n' && *ep != ' ')) + return -1; + while (*ep == ' ' || *ep == '/' || *ep == '(') + ep++; + *v2 = strtoull(ep, &ep2, 0); + if (ep2 == ep || (*ep2 != 0 && *ep2 != '\n' && *ep2 != ' ' && *ep2 != ')')) { + *v2 = *v1; + return 1; + } + return 2; +} + +int sysfs_get_two(struct mdinfo *sra, struct mdinfo *dev, + char *name, unsigned long long *v1, unsigned long long *v2) +{ + int n; + int fd; + + fd = sysfs_get_fd(sra, dev, name); + if (fd < 0) + return -1; + n = sysfs_fd_get_two(fd, v1, v2); + close(fd); + return n; +} + int sysfs_fd_get_str(int fd, char *val, int size) { int n; lseek(fd, 0, 0); n = read(fd, val, size); - if (n <= 0) + if (n <= 0 || n == size) return -1; val[n] = 0; return n; @@ -531,17 +633,30 @@ int sysfs_set_array(struct mdinfo *info, int vers) { int rv = 0; char ver[100]; + int raid_disks = info->array.raid_disks; ver[0] = 0; if (info->array.major_version == -1 && info->array.minor_version == -2) { + char buf[1024]; + strcat(strcpy(ver, "external:"), info->text_version); + /* meta version might already be set if we are setting + * new geometry for a reshape. In that case we don't + * want to over-write the 'readonly' flag that is + * stored in the metadata version. So read the current + * version first, and preserve the flag + */ + if (sysfs_get_str(info, NULL, "metadata_version", + buf, 1024) > 0) + if (strlen(buf) >= 9 && buf[9] == '-') + ver[9] = '-'; + if ((vers % 100) < 2 || sysfs_set_str(info, NULL, "metadata_version", ver) < 0) { - fprintf(stderr, Name ": This kernel does not " - "support external metadata.\n"); + pr_err("This kernel does not support external metadata.\n"); return 1; } } @@ -549,7 +664,9 @@ int sysfs_set_array(struct mdinfo *info, int vers) return 0; /* FIXME */ rv |= sysfs_set_str(info, NULL, "level", map_num(pers, info->array.level)); - rv |= sysfs_set_num(info, NULL, "raid_disks", info->array.raid_disks); + if (info->reshape_active && info->delta_disks != UnSet) + raid_disks -= info->delta_disks; + rv |= sysfs_set_num(info, NULL, "raid_disks", raid_disks); rv |= sysfs_set_num(info, NULL, "chunk_size", info->array.chunk_size); rv |= sysfs_set_num(info, NULL, "layout", info->array.layout); rv |= sysfs_set_num(info, NULL, "component_size", info->component_size/2); @@ -559,9 +676,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) { - fprintf(stderr, Name ": 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; @@ -574,10 +689,23 @@ int sysfs_set_array(struct mdinfo *info, int vers) rv |= sysfs_set_num(info, NULL, "reshape_position", info->reshape_progress); rv |= sysfs_set_num(info, NULL, "chunk_size", info->new_chunk); - /* Don't set layout or raid_disks here as they require some - * analysis and are set by reshape_array + rv |= sysfs_set_num(info, NULL, "layout", info->new_layout); + rv |= sysfs_set_num(info, NULL, "raid_disks", + info->array.raid_disks); + /* We don't set 'new_level' here. That can only happen + * 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; } @@ -587,6 +715,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); @@ -594,13 +723,7 @@ int sysfs_add_disk(struct mdinfo *sra, struct mdinfo *sd, int resume) return rv; memset(nm, 0, sizeof(nm)); - sprintf(dv, "/sys/dev/block/%d:%d", sd->disk.major, sd->disk.minor); - rv = readlink(dv, nm, sizeof(nm)); - if (rv <= 0) - return -1; - nm[rv] = '\0'; - dname = strrchr(nm, '/'); - if (dname) dname++; + dname = devid2kname(makedev(sd->disk.major, sd->disk.minor)); strcpy(sd->sys_name, "dev-"); strcpy(sd->sys_name+4, dname); @@ -614,6 +737,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. @@ -624,6 +751,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; } @@ -636,7 +785,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; @@ -671,7 +820,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'; @@ -698,77 +847,66 @@ int sysfs_disk_to_scsi_id(int fd, __u32 *id) /* from an open block device, try to retrieve it scsi_id */ struct stat st; char path[256]; - char *c1, *c2; DIR *dir; struct dirent *de; + int host, bus, target, lun; if (fstat(fd, &st)) return 1; - snprintf(path, sizeof(path), "/sys/dev/block/%d:%d/device", + snprintf(path, sizeof(path), "/sys/dev/block/%d:%d/device/scsi_device", major(st.st_rdev), minor(st.st_rdev)); dir = opendir(path); if (!dir) return 1; - de = readdir(dir); - while (de) { - if (strncmp("scsi_disk:", de->d_name, - strlen("scsi_disk:")) == 0) + for (de = readdir(dir); de; de = readdir(dir)) { + int count; + + if (de->d_type != DT_DIR) + continue; + + count = sscanf(de->d_name, "%d:%d:%d:%d", &host, &bus, &target, &lun); + if (count == 4) break; - de = readdir(dir); } closedir(dir); if (!de) return 1; - c1 = strchr(de->d_name, ':'); - c1++; - c2 = strchr(c1, ':'); - *c2 = '\0'; - *id = strtol(c1, NULL, 10) << 24; /* host */ - c1 = c2 + 1; - c2 = strchr(c1, ':'); - *c2 = '\0'; - *id |= strtol(c1, NULL, 10) << 16; /* channel */ - c1 = c2 + 1; - c2 = strchr(c1, ':'); - *c2 = '\0'; - *id |= strtol(c1, NULL, 10) << 8; /* lun */ - c1 = c2 + 1; - *id |= strtol(c1, NULL, 10); /* id */ - + *id = (host << 24) | (bus << 16) | (target << 8) | (lun << 0); return 0; } - -int sysfs_unique_holder(int devnum, long rdev) +int sysfs_unique_holder(char *devnm, long rdev) { - /* Check that devnum is a holder of rdev, + /* Check that devnm is a holder of rdev, * and is the only holder. * we should be locked against races by - * an O_EXCL on devnum + * an O_EXCL on devnm + * Return values: + * 0 - not unique, not even a holder + * 1 - unique, this is the only holder. + * 2/3 - not unique, there is another holder + * -1 - error, cannot find the holders */ DIR *dir; struct dirent *de; char dirname[100]; char l; - int found = 0; + int ret = 0; sprintf(dirname, "/sys/dev/block/%d:%d/holders", major(rdev), minor(rdev)); dir = opendir(dirname); - errno = ENOENT; if (!dir) - return 0; + return -1; l = strlen(dirname); while ((de = readdir(dir)) != NULL) { - char buf[10]; + char buf[100]; + char *sl; int n; - int mj, mn; - char c; - int fd; if (de->d_ino == 0) continue; @@ -776,41 +914,28 @@ int sysfs_unique_holder(int devnum, long rdev) continue; strcpy(dirname+l, "/"); strcat(dirname+l, de->d_name); - strcat(dirname+l, "/dev"); - fd = open(dirname, O_RDONLY); - if (fd < 0) { - errno = ENOENT; - break; - } - n = read(fd, buf, sizeof(buf)-1); - close(fd); + n = readlink(dirname, buf, sizeof(buf)-1); + if (n <= 0) + continue; buf[n] = 0; - if (sscanf(buf, "%d:%d%c", &mj, &mn, &c) != 3 || - c != '\n') { - errno = ENOENT; - break; - } - if (mj != MD_MAJOR) - mn = -1-(mn>>6); + sl = strrchr(buf, '/'); + if (!sl) + continue; + sl++; - if (devnum != mn) { - errno = EEXIST; - break; - } - found = 1; + if (strcmp(devnm, sl) == 0) + ret |= 1; + else + ret |= 2; } closedir(dir); - if (de) - return 0; - else - return found; + return ret; } int sysfs_freeze_array(struct mdinfo *sra) { /* Try to freeze resync/rebuild on this array/container. * Return -1 if the array is busy, - * return -2 container cannot be frozen, * return 0 if this kernel doesn't support 'frozen' * return 1 if it worked. */ @@ -820,113 +945,44 @@ int sysfs_freeze_array(struct mdinfo *sra) return 1; /* no sync_action == frozen */ if (sysfs_get_str(sra, NULL, "sync_action", buf, 20) <= 0) return 0; - if (strcmp(buf, "idle\n") != 0 && - strcmp(buf, "frozen\n") != 0) + if (strcmp(buf, "frozen\n") == 0) + /* Already frozen */ + return 0; + if (strcmp(buf, "idle\n") != 0 && strcmp(buf, "recover\n") != 0) return -1; if (sysfs_set_str(sra, NULL, "sync_action", "frozen") < 0) return 0; return 1; } -#ifndef MDASSEMBLE - -static char *clean_states[] = { - "clear", "inactive", "readonly", "read-auto", "clean", NULL }; - -int WaitClean(char *dev, int sock, int verbose) +int sysfs_wait(int fd, int *msec) { - int fd; - struct mdinfo *mdi; - int rv = 1; - int devnum; - - fd = open(dev, O_RDONLY); - if (fd < 0) { - if (verbose) - fprintf(stderr, Name ": Couldn't open %s: %s\n", dev, strerror(errno)); - return 1; - } - - devnum = fd2devnum(fd); - mdi = sysfs_read(fd, devnum, GET_VERSION|GET_LEVEL|GET_SAFEMODE); - if (!mdi) { - if (verbose) - fprintf(stderr, Name ": Failed to read sysfs attributes for " - "%s\n", dev); - close(fd); - return 0; - } - - switch(mdi->array.level) { - case LEVEL_LINEAR: - case LEVEL_MULTIPATH: - case 0: - /* safemode delay is irrelevant for these levels */ - rv = 0; - - } - - /* for internal metadata the kernel handles the final clean - * transition, containers can never be dirty + /* Wait up to '*msec' for fd to have an exception condition. + * if msec == NULL, wait indefinitely. */ - if (!is_subarray(mdi->text_version)) - rv = 0; - - /* safemode disabled ? */ - if (mdi->safe_mode_delay == 0) - rv = 0; - - if (rv) { - int state_fd = sysfs_open(fd2devnum(fd), NULL, "array_state"); - char buf[20]; - fd_set fds; - struct timeval tm; - - /* minimize the safe_mode_delay and prepare to wait up to 5s - * for writes to quiesce - */ - sysfs_set_safemode(mdi, 1); - tm.tv_sec = 5; - tm.tv_usec = 0; - - FD_ZERO(&fds); - - /* wait for array_state to be clean */ - while (1) { - rv = read(state_fd, buf, sizeof(buf)); - if (rv < 0) - break; - if (sysfs_match_word(buf, clean_states) <= 4) - break; - FD_SET(state_fd, &fds); - rv = select(state_fd + 1, NULL, NULL, &fds, &tm); - if (rv < 0 && errno != EINTR) - break; - lseek(state_fd, 0, SEEK_SET); + fd_set fds; + int n; + FD_ZERO(&fds); + FD_SET(fd, &fds); + if (msec == NULL) + n = select(fd+1, NULL, NULL, &fds, NULL); + else if (*msec < 0) + n = 0; + else { + struct timeval start, end, tv; + gettimeofday(&start, NULL); + if (*msec < 1000) { + tv.tv_sec = 0; + tv.tv_usec = (*msec)*1000; + } else { + tv.tv_sec = (*msec)/1000; + tv.tv_usec = 0; } - if (rv < 0) - rv = 1; - else if (fping_monitor(sock) == 0 || - ping_monitor(mdi->text_version) == 0) { - /* we need to ping to close the window between array - * state transitioning to clean and the metadata being - * marked clean - */ - rv = 0; - } else - rv = 1; - if (rv && verbose) - fprintf(stderr, Name ": Error waiting for %s to be clean\n", - dev); - - /* restore the original safe_mode_delay */ - sysfs_set_safemode(mdi, mdi->safe_mode_delay); - close(state_fd); + 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/1000) + 1; } - - sysfs_free(mdi); - close(fd); - - return rv; + return n; } -#endif /* MDASSEMBLE */