summaryrefslogtreecommitdiffstats
path: root/sysfs.c
diff options
context:
space:
mode:
authorNeil Brown <neilb@suse.de>2006-03-13 05:51:32 +0000
committerNeil Brown <neilb@suse.de>2006-03-13 05:51:32 +0000
commite86c9dd6d847ec57ec400b118efaf2c1808f10bc (patch)
tree6cb0e2d0c2e48d102b08b99d41941ae7f215a90c /sysfs.c
parent8a4440794afbebe6bdc710eefd21c221c9046644 (diff)
downloadmdadm-e86c9dd6d847ec57ec400b118efaf2c1808f10bc.tar.gz
mdadm-e86c9dd6d847ec57ec400b118efaf2c1808f10bc.tar.xz
mdadm-e86c9dd6d847ec57ec400b118efaf2c1808f10bc.zip
Initial reshape support
Needs work for other levels etc. Signed-off-by: Neil Brown <neilb@suse.de>
Diffstat (limited to 'sysfs.c')
-rw-r--r--sysfs.c265
1 files changed, 265 insertions, 0 deletions
diff --git a/sysfs.c b/sysfs.c
new file mode 100644
index 0000000..9894760
--- /dev/null
+++ b/sysfs.c
@@ -0,0 +1,265 @@
+/*
+ * sysfs - extract md related information from sysfs. Part of:
+ * mdadm - manage Linux "md" devices aka RAID arrays.
+ *
+ * Copyright (C) 2006 Neil Brown <neilb@suse.de>
+ *
+ *
+ * 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: <neilb@suse.de>
+ */
+
+#include "mdadm.h"
+#include <dirent.h>
+
+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;
+}
+
+struct sysarray *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 sysarray *sra;
+ struct sysdev *dev;
+ DIR *dir;
+ struct dirent *de;
+
+ sra = malloc(sizeof(*sra));
+ if (sra == NULL)
+ return sra;
+
+ if (fd >= 0) {
+ struct stat stb;
+ if (fstat(fd, &stb)) return NULL;
+ if (major(stb.st_rdev)==9)
+ sprintf(sra->name, "md%d", minor(stb.st_rdev));
+ else
+ sprintf(sra->name, "md_d%d",
+ minor(stb.st_rdev)/16);
+ } else {
+ if (devnum >= 0)
+ sprintf(sra->name, "md%d", devnum);
+ else
+ sprintf(sra->name, "md_d%d",
+ -1-devnum);
+ }
+ sprintf(fname, "/sys/block/%s/md/", sra->name);
+ base = fname + strlen(fname);
+
+ sra->devs = NULL;
+ if (options & GET_LEVEL) {
+ strcpy(base, "level");
+ if (load_sys(fname, buf))
+ goto abort;
+ sra->level = map_name(pers, buf);
+ }
+ if (options & GET_LAYOUT) {
+ strcpy(base, "layout");
+ if (load_sys(fname, buf))
+ goto abort;
+ sra->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);
+ }
+ if (options & GET_CHUNK) {
+ strcpy(base, "chunk_size");
+ if (load_sys(fname, buf))
+ goto abort;
+ sra->chunk = 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->spares = 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;
+
+ /* Always get slot, major, minor */
+ strcpy(dbase, "slot");
+ if (load_sys(fname, buf))
+ goto abort;
+ dev->role = strtoul(buf, &ep, 10);
+ if (*ep) dev->role = -1;
+
+ strcpy(dbase, "block/dev");
+ if (load_sys(fname, buf))
+ goto abort;
+ sscanf(buf, "%d:%d", &dev->major, &dev->minor);
+
+ if (options & GET_OFFSET) {
+ strcpy(dbase, "offset");
+ if (load_sys(fname, buf))
+ goto abort;
+ dev->offset = strtoull(buf, NULL, 0);
+ }
+ if (options & GET_SIZE) {
+ strcpy(dbase, "size");
+ if (load_sys(fname, buf))
+ goto abort;
+ dev->size = strtoull(buf, NULL, 0);
+ }
+ if (options & GET_STATE) {
+ dev->state = 0;
+ strcpy(dbase, "state");
+ if (load_sys(fname, buf))
+ goto abort;
+ if (strstr(buf, "in_sync"))
+ dev->state |= (1<<MD_DISK_SYNC);
+ if (strstr(buf, "faulty"))
+ dev->state |= (1<<MD_DISK_FAULTY);
+ if (dev->state == 0)
+ sra->spares++;
+ }
+ if (options & GET_ERROR) {
+ strcpy(buf, "errors");
+ if (load_sys(fname, buf))
+ goto abort;
+ dev->errors = strtoul(buf, NULL, 0);
+ }
+ }
+ return sra;
+
+ abort:
+ while (sra && sra->devs) {
+ dev = sra->devs;
+ sra->devs = dev->next;
+ free(dev);
+ }
+ if(sra) 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
+ */
+ 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",
+ minor(stb.st_rdev));
+ else
+ sprintf(fname, "/sys/block/md_d%d/md/component_size",
+ minor(stb.st_rdev)/16);
+ 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);
+}
+
+int sysfs_set_str(struct sysarray *sra, struct sysdev *dev,
+ char *name, char *val)
+{
+ char fname[50];
+ int n;
+ int fd;
+ sprintf(fname, "/sys/block/%s/md/%s/%s",
+ sra->name, dev?dev->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 sysarray *sra, struct sysdev *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 sysarray *sra, struct sysdev *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->name, dev?dev->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;
+}