/* * sysfs - extract md related information from sysfs. Part of: * mdadm - manage Linux "md" devices aka RAID arrays. * * Copyright (C) 2006 Neil Brown * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Neil Brown * Email: */ #include "mdadm.h" #include int load_sys(char *path, char *buf) { int fd = open(path, O_RDONLY); int n; if (fd < 0) return -1; n = read(fd, buf, 1024); close(fd); if (n <=0 || n >= 1024) return -1; buf[n] = 0; if (buf[n-1] == '\n') buf[n-1] = 0; return 0; } void sysfs_free(struct mdinfo *sra) { while (sra) { struct mdinfo *sra2 = sra->next; while (sra->devs) { struct mdinfo *d = sra->devs; sra->devs = d->next; free(d); } free(sra); sra = sra2; } } struct mdinfo *sysfs_read(int fd, int devnum, unsigned long options) { /* Longest possible name in sysfs, mounted at /sys, is * /sys/block/md_dXXX/md/dev-XXXXX/block/dev * /sys/block/md_dXXX/md/metadata_version * which is about 41 characters. 50 should do for now */ char fname[50]; char buf[1024]; char *base; char *dbase; struct mdinfo *sra; struct mdinfo *dev; DIR *dir; struct dirent *de; sra = malloc(sizeof(*sra)); if (sra == NULL) return sra; sra->next = NULL; if (fd >= 0) { struct stat stb; mdu_version_t vers; if (fstat(fd, &stb)) return NULL; if (ioctl(fd, RAID_VERSION, &vers) != 0) return NULL; if (major(stb.st_rdev)==9) sprintf(sra->sys_name, "md%d", (int)minor(stb.st_rdev)); else sprintf(sra->sys_name, "md_d%d", (int)minor(stb.st_rdev)>>MdpMinorShift); } else { if (devnum >= 0) sprintf(sra->sys_name, "md%d", devnum); else sprintf(sra->sys_name, "md_d%d", -1-devnum); } sprintf(fname, "/sys/block/%s/md/", sra->sys_name); base = fname + strlen(fname); sra->devs = NULL; if (options & GET_VERSION) { strcpy(base, "metadata_version"); if (load_sys(fname, buf)) goto abort; if (strncmp(buf, "none", 4) == 0) { sra->array.major_version = sra->array.minor_version = -1; strcpy(sra->text_version, ""); } else if (strncmp(buf, "external:", 9) == 0) { sra->array.major_version = -1; sra->array.minor_version = -2; strcpy(sra->text_version, buf+9); } else sscanf(buf, "%d.%d", &sra->array.major_version, &sra->array.minor_version); } if (options & GET_LEVEL) { strcpy(base, "level"); if (load_sys(fname, buf)) goto abort; sra->array.level = map_name(pers, buf); } if (options & GET_LAYOUT) { strcpy(base, "layout"); if (load_sys(fname, buf)) goto abort; sra->array.layout = strtoul(buf, NULL, 0); } if (options & GET_COMPONENT) { strcpy(base, "component_size"); if (load_sys(fname, buf)) goto abort; sra->component_size = strtoull(buf, NULL, 0); /* sysfs reports "K", but we want sectors */ sra->component_size *= 2; } if (options & GET_CHUNK) { strcpy(base, "chunk_size"); if (load_sys(fname, 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 (options & GET_MISMATCH) { strcpy(base, "mismatch_cnt"); if (load_sys(fname, buf)) goto abort; sra->mismatch_cnt = strtoul(buf, NULL, 0); } if (! (options & GET_DEVS)) return sra; /* Get all the devices as well */ *base = 0; dir = opendir(fname); if (!dir) goto abort; sra->array.spare_disks = 0; while ((de = readdir(dir)) != NULL) { char *ep; if (de->d_ino == 0 || strncmp(de->d_name, "dev-", 4) != 0) continue; strcpy(base, de->d_name); dbase = base + strlen(base); *dbase++ = '/'; dev = malloc(sizeof(*dev)); if (!dev) goto abort; dev->next = sra->devs; sra->devs = dev; strcpy(dev->sys_name, de->d_name); /* Always get slot, major, minor */ strcpy(dbase, "slot"); if (load_sys(fname, buf)) goto abort; dev->disk.raid_disk = strtoul(buf, &ep, 10); if (*ep) dev->disk.raid_disk = -1; strcpy(dbase, "block/dev"); if (load_sys(fname, buf)) goto abort; sscanf(buf, "%d:%d", &dev->disk.major, &dev->disk.minor); if (options & GET_OFFSET) { strcpy(dbase, "offset"); if (load_sys(fname, buf)) goto abort; dev->data_offset = strtoull(buf, NULL, 0); } if (options & GET_SIZE) { strcpy(dbase, "size"); if (load_sys(fname, buf)) goto abort; dev->component_size = strtoull(buf, NULL, 0); } if (options & GET_STATE) { dev->disk.state = 0; strcpy(dbase, "state"); if (load_sys(fname, buf)) goto abort; if (strstr(buf, "in_sync")) dev->disk.state |= (1<disk.state |= (1<disk.state == 0) sra->array.spare_disks++; } if (options & GET_ERROR) { strcpy(buf, "errors"); if (load_sys(fname, buf)) goto abort; dev->errors = strtoul(buf, NULL, 0); } } return sra; abort: sysfs_free(sra); return NULL; } unsigned long long get_component_size(int fd) { /* Find out the component size of the array. * We cannot trust GET_ARRAY_INFO ioctl as it's * size field is only 32bits. * So look in /sys/block/mdXXX/md/component_size * * This returns in units of sectors. */ struct stat stb; char fname[50]; int n; if (fstat(fd, &stb)) return 0; if (major(stb.st_rdev) == 9) 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); fd = open(fname, O_RDONLY); if (fd < 0) return 0; n = read(fd, fname, sizeof(fname)); close(fd); if (n == sizeof(fname)) return 0; fname[n] = 0; return strtoull(fname, NULL, 10) * 2; } int sysfs_set_str(struct mdinfo *sra, struct mdinfo *dev, char *name, char *val) { char fname[50]; int n; int fd; sprintf(fname, "/sys/block/%s/md/%s/%s", sra->sys_name, dev?dev->sys_name:"", name); fd = open(fname, O_WRONLY); if (fd < 0) return -1; n = write(fd, val, strlen(val)); close(fd); if (n != strlen(val)) return -1; return 0; } int sysfs_set_num(struct mdinfo *sra, struct mdinfo *dev, char *name, unsigned long long val) { char valstr[50]; sprintf(valstr, "%llu", val); return sysfs_set_str(sra, dev, name, valstr); } int sysfs_get_ll(struct mdinfo *sra, struct mdinfo *dev, char *name, unsigned long long *val) { char fname[50]; char buf[50]; int n; int fd; char *ep; sprintf(fname, "/sys/block/%s/md/%s/%s", sra->sys_name, dev?dev->sys_name:"", name); fd = open(fname, O_RDONLY); if (fd < 0) return -1; n = read(fd, buf, sizeof(buf)); close(fd); if (n <= 0) return -1; buf[n] = 0; *val = strtoull(buf, &ep, 0); if (ep == buf || (*ep != 0 && *ep != '\n' && *ep != ' ')) return -1; return 0; }