| /* |
| * sysfs based topology -- gathers topology information from Linux sysfs |
| * |
| * Copyright (C) 2009 Karel Zak <kzak@redhat.com> |
| * |
| * This file may be redistributed under the terms of the |
| * GNU Lesser General Public License. |
| * |
| * For more information see Linux kernel Documentation/ABI/testing/sysfs-block. |
| */ |
| #include <stdio.h> |
| #include <string.h> |
| #include <stdlib.h> |
| #include <inttypes.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <unistd.h> |
| #include <errno.h> |
| |
| #include "sysfs.h" |
| #include "topology.h" |
| |
| /* |
| * Sysfs topology values (since 2.6.31, May 2009). |
| */ |
| static struct topology_val { |
| |
| /* /sys/dev/block/<maj>:<min>/<ATTR> */ |
| const char *attr; |
| |
| /* functions to set probing result */ |
| int (*set_ulong)(blkid_probe, unsigned long); |
| int (*set_int)(blkid_probe, int); |
| |
| } topology_vals[] = { |
| { "alignment_offset", NULL, blkid_topology_set_alignment_offset }, |
| { "queue/minimum_io_size", blkid_topology_set_minimum_io_size }, |
| { "queue/optimal_io_size", blkid_topology_set_optimal_io_size }, |
| { "queue/physical_block_size", blkid_topology_set_physical_sector_size }, |
| }; |
| |
| static int probe_sysfs_tp(blkid_probe pr, |
| const struct blkid_idmag *mag __attribute__((__unused__))) |
| { |
| dev_t dev; |
| int rc, set_parent = 1; |
| struct path_cxt *pc; |
| size_t i, count = 0; |
| |
| dev = blkid_probe_get_devno(pr); |
| if (!dev) |
| return 1; |
| pc = ul_new_sysfs_path(dev, NULL, NULL); |
| if (!pc) |
| return 1; |
| |
| rc = 1; /* nothing (default) */ |
| |
| for (i = 0; i < ARRAY_SIZE(topology_vals); i++) { |
| struct topology_val *val = &topology_vals[i]; |
| int ok = ul_path_access(pc, F_OK, val->attr) == 0; |
| |
| rc = 1; /* nothing */ |
| |
| if (!ok && set_parent) { |
| dev_t disk = blkid_probe_get_wholedisk_devno(pr); |
| set_parent = 0; |
| |
| /* |
| * Read attributes from "disk" if the current device is |
| * a partition. Note that sysfs ul_path_* API is able |
| * to redirect requests to attributes if parent is set. |
| */ |
| if (disk && disk != dev) { |
| struct path_cxt *parent = ul_new_sysfs_path(disk, NULL, NULL); |
| if (!parent) |
| goto done; |
| |
| sysfs_blkdev_set_parent(pc, parent); |
| ul_unref_path(parent); |
| |
| /* try it again */ |
| ok = ul_path_access(pc, F_OK, val->attr) == 0; |
| } |
| } |
| if (!ok) |
| continue; /* attribute does not exist */ |
| |
| if (val->set_ulong) { |
| uint64_t data; |
| |
| if (ul_path_read_u64(pc, &data, val->attr) != 0) |
| continue; |
| rc = val->set_ulong(pr, (unsigned long) data); |
| |
| } else if (val->set_int) { |
| int64_t data; |
| |
| if (ul_path_read_s64(pc, &data, val->attr) != 0) |
| continue; |
| rc = val->set_int(pr, (int) data); |
| } |
| |
| if (rc < 0) |
| goto done; /* error */ |
| if (rc == 0) |
| count++; |
| } |
| |
| done: |
| ul_unref_path(pc); /* unref pc and parent */ |
| if (count) |
| return 0; /* success */ |
| return rc; /* error or nothing */ |
| } |
| |
| const struct blkid_idinfo sysfs_tp_idinfo = |
| { |
| .name = "sysfs", |
| .probefunc = probe_sysfs_tp, |
| .magics = BLKID_NONE_MAGIC |
| }; |
| |