From 86c5b90a76fe07731224d3b47c08721c30b41f29 Mon Sep 17 00:00:00 2001 From: mananth Date: Wed, 15 Oct 2003 12:22:43 +0000 Subject: Added sysfs_block.c --- ChangeLog | 4 + cmd/systool.c | 125 ++++++++++++++++++++-- include/libsysfs.h | 36 +++++++ lib/Makefile.am | 2 +- lib/Makefile.in | 10 +- lib/sysfs_block.c | 309 +++++++++++++++++++++++++++++++++++++++++++++++++++++ lib/sysfs_class.c | 2 +- lib/sysfs_dir.c | 3 +- 8 files changed, 474 insertions(+), 17 deletions(-) create mode 100644 lib/sysfs_block.c diff --git a/ChangeLog b/ChangeLog index a944955..8a8a97e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,4 +1,8 @@ +09/29/2003 - Ananth Mavinakayanahalli + * Added "block" subsystem support + * Modified systool to use sysfs_root_device + 09/24/2003 - Ananth Mavinakayanahalli * Added "write" attribute functions specific to device, driver and class device. diff --git a/cmd/systool.c b/cmd/systool.c index fe5e903..6ebbe3a 100644 --- a/cmd/systool.c +++ b/cmd/systool.c @@ -45,7 +45,7 @@ static unsigned char *device_to_show = NULL; /* show only this bus device */ #define SHOW_ALL 0xff -static unsigned char cmd_options[] = "aA:b:c:dDhr:v"; +static unsigned char cmd_options[] = "aA:b:B:c:dDhr:v"; /* * binary_files - defines existing sysfs binary files. These files will be @@ -73,6 +73,7 @@ void usage(void) "\t-r \tShow a specific root device tree\n"); fprintf(stdout, "\t-v\t\t\tShow all attributes with values\n"); fprintf(stdout, "\t-A \tShow attribute value\n"); + fprintf(stdout, "\t-B \t Show specific block device\n"); fprintf(stdout, "\t-D\t\t\tShow only drivers\n"); } @@ -456,7 +457,8 @@ int show_sysfs_class(unsigned char *classname) */ int show_sysfs_root(unsigned char *rootname) { - struct sysfs_device *root = NULL; + struct sysfs_root_device *root = NULL; + struct sysfs_device *device = NULL; unsigned char path[SYSFS_PATH_MAX]; if (rootname == NULL) { @@ -472,18 +474,103 @@ int show_sysfs_root(unsigned char *rootname) strcat(path, SYSFS_DEVICES_DIR); strcat(path, "/"); strcat(path, rootname); - root = sysfs_open_device_tree(path); + + root = sysfs_open_root_device(rootname); if (root == NULL) { fprintf(stderr, "Error opening root device %s\n", rootname); return 1; } - fprintf(stdout, "Root Device Tree: %s\n", rootname); - show_device_tree(root, 2); - sysfs_close_device_tree(root); + fprintf(stdout, "Root Device Tree: %s\n", rootname); + + if (root->devices) { + dlist_for_each_data(root->devices, device, + struct sysfs_device) { + show_device_tree(device, 2); + } + } + sysfs_close_root_device(root); + return 0; } +/** + * show_block_partitions: show partition details + * @partitions: dlist of partitions + * returns nothing + */ +void show_block_partitions(struct dlist *partitions, int level) +{ + struct sysfs_block_partition *part = NULL; + struct dlist *attributes = NULL; + + if (partitions == NULL) + return; + indent(level); + fprintf(stdout, "Partitions:\n"); + dlist_for_each_data(partitions, part, struct sysfs_block_partition) { + indent(level+4); + fprintf(stdout, "%s\n", part->name); + if (show_options & (SHOW_ATTRIBUTES | SHOW_ATTRIBUTE_VALUE + | SHOW_ALL_ATTRIB_VALUES)) { + attributes = sysfs_get_partition_attributes(part); + if (attributes != NULL) + show_attributes(attributes, level+8); + } + } +} + +/** + * show_sysfs_block: prints out details of the given block device + * @blockname: name of the block device to show + * returns 0 on success, 1 on error + */ +int show_sysfs_block(unsigned char *blockname) +{ + struct sysfs_block_device *block = NULL; + struct dlist *attributes = NULL; + + if (blockname == NULL) { + errno = EINVAL; + return 1; + } + + block = sysfs_open_block_device(blockname); + if (block == NULL) { + fprintf(stderr, "Error opening block device %s\n", blockname); + return 1; + } + + fprintf(stdout, "Block: %s\n", blockname); + if (show_options & (SHOW_ATTRIBUTES | SHOW_ATTRIBUTE_VALUE + | SHOW_ALL_ATTRIB_VALUES)) { + attributes = sysfs_get_blockdev_attributes(block); + if (attributes != NULL) + show_attributes(attributes, 4); + attributes = sysfs_get_queue_attributes(block); + if (attributes != NULL) { + indent(4); + fprintf(stdout, "queue:\n"); + show_attributes(attributes, 8); + } + attributes = sysfs_get_iosched_attributes(block); + if (attributes != NULL) { + indent(8); + fprintf(stdout, "iosched:\n"); + show_attributes(attributes, 12); + } + } + if (block->partitions != NULL) + show_block_partitions(block->partitions, 4); + if (block->device != NULL) { + indent(4); + fprintf(stdout, "Physical device:"); + show_device(block->device, 4); + } + sysfs_close_block_device(block); + + return 0; +} /** * show_default_info: prints current buses, classes, and root devices @@ -524,6 +611,15 @@ int show_default_info(void) } sysfs_close_list(list); + strcpy(subsys, SYSFS_BLOCK_DIR); + list = sysfs_open_subsystem_list(subsys); + if (list != NULL) { + fprintf(stdout, "Supported sysfs block devices:\n"); + dlist_for_each_data(list, cur, char) + fprintf(stdout, "\t%s\n", cur); + } + sysfs_close_list(list); + return retval; } @@ -533,6 +629,7 @@ int main(int argc, char *argv[]) unsigned char *show_bus = NULL; unsigned char *show_class = NULL; unsigned char *show_root = NULL; + unsigned char *show_block = NULL; int retval = 0; int opt; extern int optind; @@ -556,6 +653,9 @@ int main(int argc, char *argv[]) case 'b': show_bus = optarg; break; + case 'B': + show_block = optarg; + break; case 'c': show_class = optarg; break; @@ -602,9 +702,11 @@ int main(int argc, char *argv[]) exit(1); } - if ((show_bus == NULL && show_class == NULL && show_root == NULL) - && (show_options & (SHOW_ATTRIBUTES | SHOW_ATTRIBUTE_VALUE - | SHOW_DEVICES | SHOW_DRIVERS | SHOW_ALL_ATTRIB_VALUES))) { + if ((show_bus == NULL && show_class == NULL && + show_root == NULL && show_block == NULL) && + (show_options & (SHOW_ATTRIBUTES | + SHOW_ATTRIBUTE_VALUE | SHOW_DEVICES | + SHOW_DRIVERS | SHOW_ALL_ATTRIB_VALUES))) { fprintf(stderr, "Please specify a bus, class, or root device\n"); usage(); @@ -620,8 +722,11 @@ int main(int argc, char *argv[]) retval = show_sysfs_class(show_class); if (show_root != NULL) retval = show_sysfs_root(show_root); + if (show_block != NULL) + retval = show_sysfs_block(show_block); - if (show_bus == NULL && show_class == NULL && show_root == NULL) + if (show_bus == NULL && show_class == NULL && + show_root == NULL && show_block == NULL) retval = show_default_info(); exit(retval); diff --git a/include/libsysfs.h b/include/libsysfs.h index 2a96b6d..da0a3ee 100644 --- a/include/libsysfs.h +++ b/include/libsysfs.h @@ -33,6 +33,7 @@ #define SYSFS_PROC_MNTS "/proc/mounts" #define SYSFS_BUS_DIR "/bus" #define SYSFS_CLASS_DIR "/class" +#define SYSFS_BLOCK_DIR "/block" #define SYSFS_DEVICES_DIR "/devices" #define SYSFS_DEVICES_NAME "devices" #define SYSFS_DRIVERS_DIR "/drivers" @@ -40,6 +41,10 @@ #define SYSFS_NAME_ATTRIBUTE "name" #define SYSFS_UNKNOWN "unknown" +/* Some "block" subsystem specific #defines */ +#define SYSFS_QUEUE_NAME "queue" +#define SYSFS_IOSCHED_NAME "iosched" + #define SYSFS_PATH_MAX 255 #define SYSFS_NAME_LEN 50 #define SYSFS_BUS_ID_SIZE 20 @@ -127,6 +132,23 @@ struct sysfs_class { struct sysfs_directory *directory; }; +struct sysfs_block_device { + struct sysfs_device *device; /* the physical device */ + struct dlist *partitions; /* struct sysfs_block_partition */ + unsigned char name[SYSFS_NAME_LEN]; + unsigned char path[SYSFS_PATH_MAX]; + + /* for internal use only */ + struct sysfs_directory *directory; +}; + +struct sysfs_block_partition { + unsigned char name[SYSFS_NAME_LEN]; + + /* for internal use only */ + struct sysfs_directory *directory; +}; + #ifdef __cplusplus extern "C" { #endif @@ -226,6 +248,20 @@ extern int sysfs_find_device_class_name(unsigned char *bus_id, extern int sysfs_write_classdev_attr(unsigned char *dev, unsigned char *attrib, unsigned char *value); +/* generic sysfs block access */ +extern void sysfs_close_block_partition + (struct sysfs_block_partition *partition); +extern void sysfs_close_block_device(struct sysfs_block_device *block); +extern struct sysfs_block_device *sysfs_open_block_device(unsigned char *name); +extern struct dlist *sysfs_get_blockdev_attributes + (struct sysfs_block_device *block); +extern struct dlist *sysfs_get_partition_attributes + (struct sysfs_block_partition *part); +extern struct dlist *sysfs_get_queue_attributes + (struct sysfs_block_device *block); +extern struct dlist *sysfs_get_iosched_attributes + (struct sysfs_block_device *block); + #ifdef __cplusplus } #endif diff --git a/lib/Makefile.am b/lib/Makefile.am index d343f01..2b5049a 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -1,5 +1,5 @@ lib_LIBRARIES = libsysfs.a libsysfs_a_SOURCES = sysfs_utils.c sysfs_dir.c sysfs_bus.c sysfs_class.c \ - sysfs_device.c sysfs_driver.c sysfs.h dlist.c + sysfs_device.c sysfs_driver.c sysfs_block.c sysfs.h dlist.c INCLUDES = -I../include diff --git a/lib/Makefile.in b/lib/Makefile.in index a2e859e..2ecbd5f 100644 --- a/lib/Makefile.in +++ b/lib/Makefile.in @@ -77,7 +77,7 @@ am__quote = @am__quote@ install_sh = @install_sh@ lib_LIBRARIES = libsysfs.a libsysfs_a_SOURCES = sysfs_utils.c sysfs_dir.c sysfs_bus.c sysfs_class.c \ - sysfs_device.c sysfs_driver.c sysfs.h dlist.c + sysfs_device.c sysfs_driver.c sysfs_block.c sysfs.h dlist.c INCLUDES = -I../include subdir = lib @@ -90,7 +90,8 @@ libsysfs_a_AR = $(AR) cru libsysfs_a_LIBADD = am_libsysfs_a_OBJECTS = sysfs_utils.$(OBJEXT) sysfs_dir.$(OBJEXT) \ sysfs_bus.$(OBJEXT) sysfs_class.$(OBJEXT) \ - sysfs_device.$(OBJEXT) sysfs_driver.$(OBJEXT) dlist.$(OBJEXT) + sysfs_device.$(OBJEXT) sysfs_driver.$(OBJEXT) \ + sysfs_block.$(OBJEXT) dlist.$(OBJEXT) libsysfs_a_OBJECTS = $(am_libsysfs_a_OBJECTS) DEFS = @DEFS@ @@ -100,8 +101,8 @@ LDFLAGS = @LDFLAGS@ LIBS = @LIBS@ depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles -@AMDEP_TRUE@DEP_FILES = ./$(DEPDIR)/dlist.Po ./$(DEPDIR)/sysfs_bus.Po \ -@AMDEP_TRUE@ ./$(DEPDIR)/sysfs_class.Po \ +@AMDEP_TRUE@DEP_FILES = ./$(DEPDIR)/dlist.Po ./$(DEPDIR)/sysfs_block.Po \ +@AMDEP_TRUE@ ./$(DEPDIR)/sysfs_bus.Po ./$(DEPDIR)/sysfs_class.Po \ @AMDEP_TRUE@ ./$(DEPDIR)/sysfs_device.Po \ @AMDEP_TRUE@ ./$(DEPDIR)/sysfs_dir.Po \ @AMDEP_TRUE@ ./$(DEPDIR)/sysfs_driver.Po \ @@ -168,6 +169,7 @@ distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dlist.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sysfs_block.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sysfs_bus.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sysfs_class.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sysfs_device.Po@am__quote@ diff --git a/lib/sysfs_block.c b/lib/sysfs_block.c new file mode 100644 index 0000000..601f575 --- /dev/null +++ b/lib/sysfs_block.c @@ -0,0 +1,309 @@ +/* + * sysfs_block.c + * + * Generic block utility functions for libsysfs + * + * Copyright (C) IBM Corp. 2003 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* + * NOTES: + * Write functions to: + * Device major/minor given a device + * As of now, only the "block" device has writable attribs. + * Library has to take care of navigating to the attribute + * (may be at various directory levels) + */ + +#include "libsysfs.h" +#include "sysfs.h" + +void sysfs_del_partition(void *partition) +{ + sysfs_close_block_partition((struct sysfs_block_partition *)partition); +} + +/** + * sysfs_close_block_partition: closes a block partition + * @partition: sysfs_block_partition to close + */ +void sysfs_close_block_partition(struct sysfs_block_partition *partition) +{ + if (partition != NULL) { + if (partition->directory != NULL) +/* reuse sysfs_dir from the earlier structure - just set it to NULL here + * free it while closing sysfs_block_device->directory + * sysfs_close_directory(partition->directory);*/ + partition->directory = NULL; + } +} + +/** + * sysfs_close_block_device: closes a sysfs_block_device + * @block: sysfs_block_device structure + */ +void sysfs_close_block_device(struct sysfs_block_device *block) +{ + if (block != NULL) { + if (block->directory != NULL) + sysfs_close_directory(block->directory); + if (block->device != NULL) + sysfs_close_device(block->device); + if (block->partitions != NULL) + dlist_destroy(block->partitions); + } +} + +/** + * alloc_block_device: allocate a sysfs_block_device + * returns sysfs_block_device or NULL + */ +static struct sysfs_block_device *alloc_block_device() +{ + return (struct sysfs_block_device *) + calloc(1, sizeof(struct sysfs_block_device)); +} + +/** + * open_block_dir: opens a sysfs block directory + * returns sysfs_directory on success or NULL on error + */ +static struct sysfs_directory *open_block_dir(const unsigned char *name) +{ + unsigned char path[SYSFS_PATH_MAX]; + struct sysfs_directory *directory = NULL; + + if (name == NULL) { + errno = EINVAL; + return NULL; + } + + memset(path, 0, SYSFS_PATH_MAX); + if ((sysfs_get_mnt_path(path, SYSFS_PATH_MAX)) != 0) { + dprintf("Error getting sysfs mount path\n"); + return NULL; + } + + strcat(path, SYSFS_BLOCK_DIR); + strcat(path, "/"); + strcat(path, name); + directory = sysfs_open_directory(path); + if (directory == NULL) { + dprintf("Block device %s not supported on this system", name); + return NULL; + } + if ((sysfs_read_directory(directory)) != 0) { + dprintf("Error reading directory %s\n", directory->path); + sysfs_close_directory(directory); + return NULL; + } + sysfs_read_all_subdirs(directory); + + return directory; +} + +/** + * alloc_block_partition: alloc a sysfs_block_partition structure + */ +static struct sysfs_block_partition *alloc_block_partition(void) +{ + return(struct sysfs_block_partition *) + calloc(1, sizeof(struct sysfs_block_partition)); +} + +/** + * get_all_block_devices: Retrieves details of block directory + * @block: sysfs_block_device for which details are required + * returns 0 on success, -1 on failure + */ +static int get_all_block_devices(struct sysfs_block_device *block) +{ + struct sysfs_directory *cur = NULL; + struct sysfs_block_partition *part = NULL; + + if (block == NULL || block->directory == NULL) { + errno = EINVAL; + return -1; + } + + dlist_for_each_data(block->directory->subdirs, cur, + struct sysfs_directory) { + switch(strcmp(cur->name, SYSFS_QUEUE_NAME)) { + case 0: /* this is the "queue" directory */ + if ((sysfs_read_directory(cur)) < 0) { + dprintf("Error reading directory %s\n", + cur->path); + return -1; + } + break; + default: /* these are the partitions */ + part = alloc_block_partition(); + if (part == NULL) { + perror("calloc"); + return -1; + } + part->directory = cur; + strcpy(part->name, cur->name); + if (block->partitions == NULL) + block->partitions = + dlist_new_with_delete + (sizeof(struct + sysfs_block_partition), + sysfs_del_partition); + dlist_unshift(block->partitions, part); + break; + } + } + return 0; +} + +/** + * sysfs_open_block_device: opens the specific block device and all its related + * details as partitions, etc + * returns sysfs_block_device struct on success and NULL on error + */ +struct sysfs_block_device *sysfs_open_block_device(unsigned char *name) +{ + struct sysfs_block_device *block = NULL; + struct sysfs_directory *blockdir = NULL; + struct sysfs_link *curlink = NULL; + + if (name == NULL) { + errno = EINVAL; + return NULL; + } + + block = alloc_block_device(); + if (block == NULL) { + perror("calloc"); + return NULL; + } + strcpy(block->name, name); + blockdir = open_block_dir(name); + if (blockdir == NULL) { + sysfs_close_block_device(block); + return NULL; + } + strcpy(block->path, blockdir->path); + block->directory = blockdir; + if ((get_all_block_devices(block)) != 0) { + dprintf("Error retrieving devices for block %s\n", name); + sysfs_close_block_device(block); + return NULL; + } + /* check if the "block" device has a link to the physical device */ + if (block->directory->links != NULL) { + dlist_for_each_data(block->directory->links, curlink, + struct sysfs_link) { + block->device = sysfs_open_device(curlink->target); + if (block->device == NULL) { + dprintf("Error opening device at %s\n", + curlink->target); + } + } + } + + return block; +} + +/** + * sysfs_get_blockdev_attributes: returns attributes for the block device + * @block: block device for which attribs are to be returned + */ +struct dlist *sysfs_get_blockdev_attributes(struct sysfs_block_device *block) +{ + if (block == NULL || block->directory == NULL) + return NULL; + + return(block->directory->attributes); +} + +/** + * sysfs_get_partition_attributes: returns attributes for the block + * device partition + * @block: block device partition for which attribs are to be returned + */ +struct dlist *sysfs_get_partition_attributes + (struct sysfs_block_partition *part) +{ + if (part == NULL || part->directory == NULL) + return NULL; + + return(part->directory->attributes); +} + +/** + * sysfs_get_queue_attributes: returns attributes for the block device's + * QUEUE parameters. Used to set #of queued + * requests as well as the choice of IO + * scheduler + * @block: block device for which the attributes are needed. + */ +struct dlist *sysfs_get_queue_attributes(struct sysfs_block_device *block) +{ + struct dlist *list = NULL; + struct sysfs_directory *dir = NULL; + unsigned int found = 0; + + dlist_for_each_data(block->directory->subdirs, dir, + struct sysfs_directory) { + if ((strcmp(dir->name, SYSFS_QUEUE_NAME)) != 0) + continue; + else + return (dir->attributes); + } + return NULL; +} + +/** + * sysfs_get_iosched_attributes: returns attributes for the block device's + * IOSCHED parameters for the given device + * @block: block device for which the attributes are needed + * returns a dlist of iosched attributes. + */ +struct dlist *sysfs_get_iosched_attributes(struct sysfs_block_device *block) +{ + struct dlist *list = NULL; + struct sysfs_directory *dir = NULL, *new = NULL; + unsigned int found = 0; + + dlist_for_each_data(block->directory->subdirs, dir, + struct sysfs_directory) { + if ((strcmp(dir->name, SYSFS_QUEUE_NAME)) != 0) + continue; + else { + found = 1; + break; + } + } + if (found) { + /* + * this is the queue directory - read this and the + * iosched directory too + */ + dlist_for_each_data(dir->subdirs, new, + struct sysfs_directory) { + if ((strcmp(new->name, SYSFS_IOSCHED_NAME)) == 0) + if ((sysfs_read_directory(new)) == 0) + return new->attributes; + } + } + dprintf("IOSCHED attributes not found\n"); + return NULL; +} + diff --git a/lib/sysfs_class.c b/lib/sysfs_class.c index 6c969b5..9172a1e 100644 --- a/lib/sysfs_class.c +++ b/lib/sysfs_class.c @@ -264,7 +264,7 @@ struct sysfs_class *sysfs_open_class(const unsigned char *name) cls = alloc_class(); if (cls == NULL) { - perror("malloc"); + perror("calloc"); return NULL; } strcpy(cls->name, name); diff --git a/lib/sysfs_dir.c b/lib/sysfs_dir.c index 1d730e7..46ebd52 100644 --- a/lib/sysfs_dir.c +++ b/lib/sysfs_dir.c @@ -525,10 +525,11 @@ int sysfs_read_directory(struct sysfs_directory *sysdir) } } - if (sysdir->attributes == NULL) + if (sysdir->attributes == NULL) { sysdir->attributes = dlist_new_with_delete (sizeof(struct sysfs_attribute), sysfs_del_attribute); + } dlist_unshift(sysdir->attributes, attr); } else if (S_ISDIR(astats.st_mode)) { subdir = sysfs_open_directory(file_path); -- cgit v1.2.1