2002-10-10 20:00:35

by Kevin Corry

[permalink] [raw]
Subject: [PATCH] EVMS core (0/9)

Greetings,

On behalf of the EVMS team, I would like to submit the Enterprise Volume
Management System for review and possible inclusion in the 2.5 kernel.

This submission includes only the core driver and include files. Additional
plugins will be submitted in the future in separate patches.

This series of patches contains the following files:
1) evms.c
2) services.c
3) discover.c
4) passthru.c
5) evms_core.h
6) evms.h
7) evms_ioctl.h
8) evms_biosplit.h
9) miscellaneous files

If you are interested in other information about EVMS, or would
like to obtain the user-space administration tools, please visit
our website at http://evms.sourceforge.net/.

Thank you very much for taking the time to review this submission. If you
have any questions or comments, please email us at any time. We will be happy
to do whatever is necessary to make EVMS acceptable for inclusion in the 2.5
tree.

--
Kevin Corry
[email protected]
http://evms.sourceforge.net/


2002-10-10 20:15:27

by Kevin Corry

[permalink] [raw]
Subject: [PATCH] EVMS core (5/9) evms_core.h

Greetings,

Part 5 of the EVMS core driver.

This is a private header file for the various files
that make up the core driver.

Kevin Corry
[email protected]
http://evms.sourceforge.net/



diff -Naur linux-2.5.41/drivers/evms/core/evms_core.h linux-2.5.41-evms/drivers/evms/core/evms_core.h
--- linux-2.5.41/drivers/evms/core/evms_core.h Wed Dec 31 18:00:00 1969
+++ linux-2.5.41-evms/drivers/evms/core/evms_core.h Thu Oct 10 11:16:58 2002
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2000 - 2002
+ *
+ * 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
+ */
+
+#ifndef __EVMS_CORE_H__
+#define __EVMS_CORE_H__
+
+#define LOG_PREFIX
+
+//#define VFS_PATCH_PRESENT
+
+/**
+ * struct evms_io_notify - IO callback hook structure
+ * @rsector:
+ * @b_private:
+ * @private:
+ * @bio:
+ * @callback_function:
+ * @flags:
+ * @bdev:
+ *
+ * IO notification structure used to track each IO callback hook.
+ **/
+struct evms_io_notify {
+ u64 rsector;
+ void *b_private;
+ void *private;
+ struct bio *bio;
+ void (*callback_function)(void *private,
+ struct bio * bio, int *redrive);
+ unsigned int flags;
+ struct block_device *bdev;
+};
+/**
+ * field evms_io_notify.flags defines
+ **/
+#define EVMS_ORIGINAL_CALLBACK_FLAG (1<<0)
+
+/**
+ * struct evms_kevent
+ * @uevent
+ * @list
+ */
+struct evms_kevent {
+ struct evms_event uevent;
+ struct list_head list;
+};
+
+/* Global data shared among the core files. */
+extern struct list_head evms_logical_volumes;
+extern struct list_head evms_notify_list;
+extern struct list_head evms_fbottom_list;
+extern struct list_head plugin_head;
+extern spinlock_t plugin_lock;
+extern mempool_t *evms_io_notify_pool;
+extern struct proc_dir_entry *evms_proc_dir;
+extern struct block_device_operations evms_fops;
+extern devfs_handle_t evms_dir_devfs_handle;
+extern int evms_volumes;
+
+/* Functions shared among the core files. */
+extern struct evms_logical_volume * lookup_volume(int minor);
+extern struct evms_logical_volume * find_next_volume(struct evms_logical_volume *lv);
+extern struct evms_logical_volume * find_next_volume_safe(struct evms_logical_volume **next_lv);
+extern int evms_discover_volumes(struct evms_rediscover_pkt *);
+extern void evms_discover_logical_disks(struct list_head *);
+extern int evms_quiesce_volume(struct evms_logical_volume *volume,
+ int command, int minor, int lock_vfs);
+extern int evms_make_request_fn(request_queue_t * q, struct bio *bio);
+extern int is_busy(kdev_t dev);
+
+/* If the VFS-lock patch has not been applied, need to declare empty
+ * inline functions for fsync_dev_lockfs() and unlockfs().
+ */
+#ifndef VFS_PATCH_PRESENT
+static inline int fsync_dev_lockfs(kdev_t dev) { return 0; }
+static inline void unlockfs(kdev_t dev) { }
+#endif
+
+#endif
+
?A8qh

2002-10-10 20:15:48

by Kevin Corry

[permalink] [raw]
Subject: [PATCH] EVMS core (6/9) evms.h

Greetings,

Part 6 of the EVMS core driver.

This is the public header file for EVMS. It is included by all of the EVMS
plugins.

Kevin Corry
[email protected]
http://evms.sourceforge.net/



diff -Naur linux-2.5.41/include/linux/evms.h linux-2.5.41-evms/include/linux/evms.h
--- linux-2.5.41/include/linux/evms.h Wed Dec 31 18:00:00 1969
+++ linux-2.5.41-evms/include/linux/evms.h Thu Oct 10 11:20:25 2002
@@ -0,0 +1,551 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2000 - 2002
+ *
+ * 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
+ */
+
+#ifndef __EVMS_INCLUDED__
+#define __EVMS_INCLUDED__
+
+#include <linux/blkdev.h>
+#include <linux/kdev_t.h>
+#include <linux/slab.h>
+#include <linux/proc_fs.h>
+#include <linux/mempool.h>
+
+/**
+ * version info
+ **/
+#define EVMS_MAJOR_VERSION 1
+#define EVMS_MINOR_VERSION 2
+#define EVMS_PATCHLEVEL_VERSION 0
+
+/**
+ * general defines section
+ **/
+#define MAX_EVMS_VOLUMES 256
+#define EVMS_VOLUME_NAME_SIZE 127
+#define IBM_OEM_ID 8112
+#define EVMS_INITIAL_CRC 0xFFFFFFFF
+#define EVMS_MAGIC_CRC 0x31415926
+#define EVMS_VSECTOR_SIZE 512
+#define EVMS_VSECTOR_SIZE_SHIFT 9
+
+#define EVMS_DIR_NAME "evms"
+#define EVMS_DEV_NAME "block_device"
+#define EVMS_PRIMARY_STRING "primary"
+#define EVMS_SECONDARY_STRING "secondary"
+
+/**
+ * kernel logging levels defines
+ **/
+#define EVMS_INFO_CRITICAL 0
+#define EVMS_INFO_SERIOUS 1
+#define EVMS_INFO_ERROR 2
+#define EVMS_INFO_WARNING 3
+#define EVMS_INFO_DEFAULT 5
+#define EVMS_INFO_DETAILS 6
+#define EVMS_INFO_DEBUG 7
+#define EVMS_INFO_EXTRA 8
+#define EVMS_INFO_ENTRY_EXIT 9
+#define EVMS_INFO_EVERYTHING 10
+
+/**
+ * kernel logging level variable
+ **/
+extern int evms_info_level;
+
+/**
+ * kernel logging macros
+ **/
+#define evmsLOG(info_level,prspec) { if (evms_info_level >= info_level) printk prspec; }
+#define evmsLOG2(info_level,statement) { if (evms_info_level >= info_level) statement; }
+
+/**
+ * LOG MACROS to make evms log messages
+ * look much cleaner in the source.
+ **/
+#define EVMS_LOG_PREFIX "evms: "
+#define LOG_CRITICAL(msg, args...) evmsLOG(EVMS_INFO_CRITICAL, (KERN_CRIT EVMS_LOG_PREFIX LOG_PREFIX msg, ## args))
+#define LOG_SERIOUS(msg, args...) evmsLOG(EVMS_INFO_SERIOUS, (KERN_ERR EVMS_LOG_PREFIX LOG_PREFIX msg, ## args))
+#define LOG_ERROR(msg, args...) evmsLOG(EVMS_INFO_ERROR, (KERN_ERR EVMS_LOG_PREFIX LOG_PREFIX msg, ## args))
+#define LOG_WARNING(msg, args...) evmsLOG(EVMS_INFO_WARNING, (KERN_WARNING EVMS_LOG_PREFIX LOG_PREFIX msg, ## args))
+#define LOG_DEFAULT(msg, args...) evmsLOG(EVMS_INFO_DEFAULT, (KERN_INFO EVMS_LOG_PREFIX LOG_PREFIX msg, ## args))
+#define LOG_DETAILS(msg, args...) evmsLOG(EVMS_INFO_DETAILS, (KERN_INFO EVMS_LOG_PREFIX LOG_PREFIX msg, ## args))
+#define LOG_DEBUG(msg, args...) evmsLOG(EVMS_INFO_DEBUG, (KERN_INFO EVMS_LOG_PREFIX LOG_PREFIX msg, ## args))
+#define LOG_EXTRA(msg, args...) evmsLOG(EVMS_INFO_EXTRA, (KERN_INFO EVMS_LOG_PREFIX LOG_PREFIX msg, ## args))
+#define LOG_ENTRY_EXIT(msg, args...) evmsLOG(EVMS_INFO_ENTRY_EXIT, (KERN_INFO EVMS_LOG_PREFIX LOG_PREFIX msg, ## args))
+#define LOG_EVERYTHING(msg, args...) evmsLOG(EVMS_INFO_EVERYTHING, (KERN_INFO EVMS_LOG_PREFIX LOG_PREFIX msg, ## args))
+
+/**
+ * Macros to cleanly print 64-bit numbers on both 32-bit and 64-bit machines.
+ * Use these in place of %Ld, %Lu, and %Lx.
+ **/
+#if BITS_PER_LONG > 32
+#define PFD64 "%ld"
+#define PFU64 "%lu"
+#define PFX64 "%lx"
+#else
+#define PFD64 "%Ld"
+#define PFU64 "%Lu"
+#define PFX64 "%Lx"
+#endif
+
+/**
+ * helpful PROCFS macro
+ **/
+#ifdef CONFIG_PROC_FS
+#define PROCPRINT(msg, args...) (sz += sprintf(page + sz, msg, ## args));\
+ if (sz < off)\
+ off -= sz, sz = 0;\
+ else if (sz >= off + count)\
+ goto out
+#endif
+
+/**
+ * PluginID convenience macros
+ *
+ * An EVMS PluginID is a 32-bit number with the following bit positions:
+ * Top 16 bits: OEM identifier. See IBM_OEM_ID.
+ * Next 4 bits: Plugin type identifier. See evms_plugin_code.
+ * Lowest 12 bits: Individual plugin identifier within a given plugin type.
+ **/
+#define SetPluginID(oem, type, id) ((oem << 16) | (type << 12) | id)
+#define GetPluginOEM(pluginid) (pluginid >> 16)
+#define GetPluginType(pluginid) ((pluginid >> 12) & 0xf)
+#define GetPluginID(pluginid) (pluginid & 0xfff)
+
+/**
+ * enum evms_plugin_type - evms plugin types
+ **/
+enum evms_plugin_code {
+ EVMS_NO_PLUGIN = 0,
+ EVMS_DEVICE_MANAGER,
+ EVMS_SEGMENT_MANAGER,
+ EVMS_REGION_MANAGER,
+ EVMS_FEATURE,
+ EVMS_ASSOCIATIVE_FEATURE,
+ EVMS_FILESYSTEM_INTERFACE_MODULE,
+ EVMS_CLUSTER_MANAGER_INTERFACE_MODULE,
+ EVMS_DISTRIBUTED_LOCK_MANAGER_INTERFACE_MODULE
+};
+
+/**
+ * struct evms_version -
+ * @major: changes when incompatible difference are introduced
+ * @minor: changes when additions are made
+ * @patchlevel: reflects bug level fixes within a particular major/minor pair
+ *
+ * generic versioning info used by EVMS
+ **/
+struct evms_version {
+ u32 major;
+ u32 minor;
+ u32 patchlevel;
+};
+
+/**
+ * struct evms_plugin_header - kernel plugin header record
+ * @id: plugin id
+ * @version: plugin version
+ * @required_services_version: required common services version
+ * @fops: table of function operations
+ * @headers: list member field
+ *
+ * kernel plugin header record
+ **/
+struct evms_plugin_header {
+ u32 id;
+ struct evms_version version;
+ struct evms_plugin_fops *fops;
+ struct list_head headers;
+};
+
+/**
+ * struct evms_feature_header - EVMS generic on-disk header for features
+ * @signature: unique magic number
+ * @crc: structure's crc
+ * @version: feature header version
+ * @engine_version: created by this evms engine version
+ * @flags: feature characteristics, bit definitions below.
+ * @feature_id: indicates which feature this header is describing
+ * @sequence_number: describes most recent copy of redundant metadata
+ * @alignment_padding: used when objects are moved between different sized devices
+ * @feature_data1_start_lsn: object relative start of 1st copy feature data
+ * @feature_data1_size: size of 1st copy of feature data
+ * @feature_data2_start_lsn: object relative start of 2nd copy feature data
+ * @feature_data2_size: size of 2nd copy of feature data
+ * @volume_serial_number: unique/persistent volume identifier
+ * @volume_system_id: unique/persistent minor number
+ * @object_depth: depth of object in volume tree
+ * @object_name: object's name
+ * @volume_name: volume name object is a part of
+ * @pad: padding to make structure be 512 byte aligned
+ *
+ * generic on-disk header used to describe any EVMS feature
+ * NOTE: 2nd copy of feature data is optional, if used set start_lsn to 0.
+ **/
+struct evms_feature_header {
+ u32 signature;
+ u32 crc;
+ struct evms_version version;
+ struct evms_version engine_version;
+ u32 flags;
+ u32 feature_id;
+ u64 sequence_number;
+ u64 alignment_padding;
+ u64 feature_data1_start_lsn;
+ u64 feature_data1_size;
+ u64 feature_data2_start_lsn;
+ u64 feature_data2_size;
+ u64 volume_serial_number;
+ u32 volume_system_id;
+ u32 object_depth;
+ u8 object_name[EVMS_VOLUME_NAME_SIZE + 1];
+ u8 volume_name[EVMS_VOLUME_NAME_SIZE + 1];
+ u8 pad[152];
+};
+
+/**
+ * field evms_feature_header.signature majic number
+ **/
+#define EVMS_FEATURE_HEADER_SIGNATURE 0x54414546 /* FEAT */
+/**
+ * field evms_feature_header.flags defines
+ **/
+#define EVMS_FEATURE_ACTIVE (1<<0)
+#define EVMS_FEATURE_VOLUME_COMPLETE (1<<1)
+#define EVMS_VOLUME_DATA_OBJECT (1<<16)
+#define EVMS_VOLUME_DATA_STOP (1<<17)
+/**
+ * struct evms_feature_header version info
+ **/
+#define EVMS_FEATURE_HEADER_MAJOR 3
+#define EVMS_FEATURE_HEADER_MINOR 0
+#define EVMS_FEATURE_HEADER_PATCHLEVEL 0
+
+/**
+ * EVMS specific error codes
+ **/
+#define EVMS_FEATURE_FATAL_ERROR 257
+#define EVMS_VOLUME_FATAL_ERROR 258
+#define EVMS_FEATURE_INCOMPLETE_ERROR 259
+
+/**
+ * struct evms_volume_info - exported volume info
+ * @volume_sn: unique volume identifier
+ * @volume_minor: persistent device minor assigned to this volume
+ * @volume_name: persistent name assigned to this volume
+ *
+ * a collection of volume specific info
+ **/
+struct evms_volume_info {
+ u64 volume_sn;
+ u32 volume_minor;
+ u8 volume_name[EVMS_VOLUME_NAME_SIZE + 1];
+};
+
+/**
+ * struct evms_logical_node - generic kernel storage object
+ * @total_vsectors: 0 size of this object in 512 byte units
+ * @plugin: 8 plugin that created/owns/manages this storage object
+ * @private: 12 location for owner to store private info
+ * @flags: 16 storage object characteristics (set/used by plugins)
+ * bit definitions located in evms_common.h
+ * @iflags: 20 internal flags (used exclusively by the framework, not for plugins to use/set)
+ * bit definitions below.
+ * @hardsector_size: 24 assumed physical sector size of underlying device
+ * @block_size: 28 default block size for this object
+ * @system_id: 32 system indicator (set by the segment manager)
+ * @volume_info: 36 persistent volume info, used only by EVMS volumes
+ * @feature_header: 40 generic on-disk metadata describing any EVMS feature
+ * @next: 44 linked list field
+ * @discover: 48 discover list field
+ * @device: 56 device list field
+ * @fbottom: 64 bottom-most feature object list field
+ * @removable: 72 changed removable media list field
+ * @consumer: 80 list field for use by the object's consumer
+ * @name: 88 storage object name
+ * 216
+ *
+ * generic kernel storage object
+ */
+struct evms_logical_node {
+ u64 total_vsectors;
+ struct evms_plugin_header *plugin;
+ void *private;
+ u32 flags;
+ u32 iflags;
+ int hardsector_size;
+ int block_size;
+ u32 system_id;
+ struct evms_volume_info *volume_info;
+ struct evms_feature_header *feature_header;
+ void *consumer_private;
+ struct list_head consumed;
+ struct list_head produced;
+ struct list_head discover;
+ struct list_head device;
+ struct list_head fbottom;
+ struct list_head removable;
+ u8 name[EVMS_VOLUME_NAME_SIZE + 1];
+};
+
+/**
+ * fields evms_logical_node.flags & evms_logical_volume.flags defines
+ **/
+#define EVMS_FLAGS_WIDTH 32
+#define EVMS_VOLUME_FLAG (1<<0)
+#define EVMS_VOLUME_PARTIAL_FLAG (1<<1)
+#define EVMS_VOLUME_PARTIAL (1<<1)
+#define EVMS_VOLUME_SET_READ_ONLY (1<<2)
+#define EVMS_VOLUME_READ_ONLY (1<<2)
+/**
+ * these bits define volume status
+ **/
+#define EVMS_MEDIA_CHANGED (1<<20)
+#define EVMS_DEVICE_UNPLUGGED (1<<21)
+/**
+ * these bits used for removable status
+ **/
+#define EVMS_DEVICE_MEDIA_PRESENT (1<<24)
+#define EVMS_DEVICE_PRESENT (1<<25)
+#define EVMS_DEVICE_LOCKABLE (1<<26)
+#define EVMS_DEVICE_REMOVABLE (1<<27)
+
+/**
+ * fields evms_logical_node.iflags defines
+ **/
+#define EVMS_FEATURE_BOTTOM (1<<0)
+#define EVMS_TOP_SEGMENT (1<<1)
+
+/**
+ * macro to obtain a node's name from either EVMS or compatibility volumes
+ **/
+#define EVMS_GET_NODE_NAME(node) \
+ ((node->flags & EVMS_VOLUME_FLAG) ? \
+ node->volume_info->volume_name : \
+ node->name)
+
+/**
+ * macro used to transform to/from userland device handles and device storage object nodes
+ **/
+#define EVMS_HANDLE_KEY 0x0123456789ABCDEF
+#define DEV_HANDLE_TO_NODE(handle) ((struct evms_logical_node *)(unsigned long)((handle) ^ EVMS_HANDLE_KEY))
+#define NODE_TO_DEV_HANDLE(node) (((u64)(unsigned long)(node)) ^ EVMS_HANDLE_KEY)
+
+/**
+ * struct evms_logical_volume - logical volume info
+ * @name: logical volume name
+ * @node: logical volume storage object
+ * @minor: device minor assigned to volume
+ * @flags: characteristics of logical volume
+ * @quiesced: quiesce state info
+ * @vfs_quiesced: vfs quiesce state info
+ * @lock: volume lock state
+ * @requests_in_progress: count of in-flight I/Os
+ * @quiesce_wait_queue: ioctl thread wait queue
+ * @request_wait_queue: io request wait queue
+ * @gd: gendisk entry for the volume
+ * @request_queue: unique request queue
+ * @request_lock: unique request queue lock
+ * @volumes: list member field
+ *
+ * contains all the fields needed to manage to a logical volume
+ **/
+struct evms_logical_volume {
+ u8 *name;
+ struct evms_logical_node *node;
+ int minor;
+ int flags;
+ int quiesced;
+ int vfs_quiesced;
+ int lock;
+ atomic_t requests_in_progress;
+ wait_queue_head_t quiesce_wait_queue;
+ wait_queue_head_t request_wait_queue;
+ struct gendisk *gd;
+ request_queue_t request_queue;
+ spinlock_t request_lock;
+ struct list_head volumes;
+};
+
+/**
+ * field evms_logical_volume.flags defines
+ **/
+/**
+ * queued flags bits
+ **/
+#define EVMS_REQUESTED_DELETE (1<<5)
+#define EVMS_REQUESTED_QUIESCE (1<<6)
+#define EVMS_REQUESTED_VFS_QUIESCE (1<<7)
+/**
+ * this bit indicates corruption
+ **/
+#define EVMS_VOLUME_CORRUPT (1<<8)
+/**
+ * these bits define the source of the corruption
+ **/
+#define EVMS_VOLUME_SOFT_DELETED (1<<9)
+#define EVMS_DEVICE_UNAVAILABLE (1<<10)
+
+/*
+ * The following function table is used for all plugins.
+ */
+/**
+ * struct evms_plugin_fops - evms plugin's table of function operations
+ * @discover: volume discovery entry point
+ * @end_discover: final discovery entry point
+ * @delete: delete volume entry point
+ * @submit_io: asynchronous read/write entry point
+ * @sync_io: synchronous io entry point
+ * @ioctl: generic ioctl entry point
+ * @direct_ioctl: non-generic ioctl entry point
+ *
+ * evms plugin's table of function operations
+ **/
+struct evms_plugin_fops {
+ int (*discover) (struct list_head *);
+ int (*end_discover) (struct list_head *);
+ int (*delete) (struct evms_logical_node *);
+ void (*submit_io) (struct evms_logical_node *, struct bio *);
+ int (*sync_io) (struct evms_logical_node *, int, u64,
+ u64, void *);
+ int (*ioctl) (struct evms_logical_node *, struct inode *,
+ struct file *, u32, unsigned long);
+ int (*direct_ioctl) (struct inode *, struct file *,
+ u32, unsigned long);
+ int (*open) (struct evms_logical_node *,
+ struct inode *, struct file *);
+ int (*release) (struct evms_logical_node *,
+ struct inode *, struct file *);
+ int (*check_media_change) (struct evms_logical_node *, kdev_t);
+ int (*revalidate) (struct evms_logical_node *, kdev_t);
+ int (*quiesce) (struct evms_logical_node *, int);
+ int (*get_geo) (struct evms_logical_node *, u64 *,
+ uint *, uint *, u64 *);
+ int (*device_list) (struct evms_logical_node *, struct list_head *);
+ int (*device_status) (struct evms_logical_node *, int *);
+};
+
+/**
+ * convenience macros to use plugin's fops entry points
+ **/
+#define DISCOVER(node, list) ((plugin)->fops->discover(list))
+#define END_DISCOVER(node, list) ((plugin)->fops->end_discover(list))
+#define DELETE(node) ((node)->plugin->fops->delete(node))
+#define SUBMIT_IO(node, bio) ((node)->plugin->fops->submit_io(node, bio))
+#define INIT_IO(node, rw_flag, start_sec, num_secs, buf_addr) ((node)->plugin->fops->sync_io(node, rw_flag, start_sec, num_secs, buf_addr))
+#define IOCTL(node, inode, file, cmd, arg) ((node)->plugin->fops->ioctl(node, inode, file, cmd, arg))
+#define DIRECT_IOCTL(plugin, inode, file, cmd, arg) ((plugin)->fops->direct_ioctl(inode, file, cmd, arg))
+#define OPEN(node, inode, file) ((node)->plugin->fops->open(node, inode, file))
+#define CLOSE(node, inode, file) ((node)->plugin->fops->release(node, inode, file))
+#define CHECK_MEDIA_CHANGE(node, dev) ((node)->plugin->fops->check_media_change(node, dev))
+#define REVALIDATE(node, dev) ((node)->plugin->fops->revalidate(node, dev))
+#define QUIESCE(node, cmd) ((node)->plugin->fops->quiesce(node, cmd))
+#define GET_GEO(node, cyls, heads, sects, start) ((node)->plugin->fops->get_geo(node, cyls, heads, sects, start))
+#define DEVICE_LIST(node, list_head) ((node)->plugin->fops->device_list(node, list_head))
+#define DEVICE_STATUS(node, status) ((node)->plugin->fops->device_status(node, status))
+
+/*
+ * Notes:
+ * All of the following kernel thread functions belong to EVMS base.
+ * These functions were copied from md_core.c
+ */
+#define EVMS_THREAD_WAKEUP 0
+/**
+ * struct evms_thread
+ * @run:
+ * @data:
+ * @wqueue: thread wait queue
+ * @flags: thread attributes
+ * @event: event completion
+ * @tsk: task info
+ * @name: thread name
+ *
+ * data structure for creating/managing a kernel thread
+ **/
+struct evms_thread {
+ void (*run) (void *data);
+ void *data;
+ wait_queue_head_t wqueue;
+ unsigned long flags;
+ struct completion *event;
+ struct task_struct *tsk;
+ const u8 *name;
+};
+
+/**
+ * struct bio_split_cb - bio split control block structure definition
+ * @rc:
+ * @sector:
+ * @original_bio:
+ * @outstanding_bios:
+ * @pool:
+ *
+ * control block for managing bio splitting
+ **/
+struct bio_split_cb {
+ int rc;
+ u64 sector;
+ struct bio *original_bio;
+ atomic_t outstanding_bios;
+ mempool_t *bio_pool;
+ mempool_t *split_pool;
+};
+
+/**
+ * EVMS (common services) exported functions prototypes
+ *
+ * since these function names are global, evms_cs_ has been prepended
+ * to each function name, to ensure they do not collide with any
+ * other global functions in the kernel.
+ **/
+#define EVMS_COMMON_SERVICES_MAJOR 0
+#define EVMS_COMMON_SERVICES_MINOR 6
+#define EVMS_COMMON_SERVICES_PATCHLEVEL 0
+
+int evms_cs_check_version(struct evms_version *, struct evms_version *);
+int evms_cs_allocate_logical_node(struct evms_logical_node **);
+void evms_cs_deallocate_volume_info(struct evms_logical_node *);
+void evms_cs_deallocate_logical_node(struct evms_logical_node *);
+int evms_cs_register_plugin(struct evms_plugin_header *);
+int evms_cs_unregister_plugin(struct evms_plugin_header *);
+int evms_cs_kernel_ioctl(struct evms_logical_node *, u32, unsigned long);
+inline unsigned long evms_cs_size_in_vsectors(long long);
+inline int evms_cs_log2(long long);
+u32 evms_cs_calculate_crc(u32, void *, u32);
+int evms_cs_register_for_end_io_notification(void *,
+ struct bio *,
+ void *callback_function);
+void evms_cs_signal_event(int);
+struct evms_thread *evms_cs_register_thread(void (*run) (void *),
+ void *data, const u8 *name);
+void evms_cs_unregister_thread(struct evms_thread *thread);
+void evms_cs_wakeup_thread(struct evms_thread *thread);
+void evms_cs_interrupt_thread(struct evms_thread *thread);
+struct proc_dir_entry *evms_cs_get_evms_proc_dir(void);
+int evms_cs_volume_request_in_progress(kdev_t, int, int *);
+void evms_cs_invalidate_volume(struct evms_logical_node *topmost_node);
+int evms_cs_split_bio(struct bio *, u64, struct bio **, struct bio **,
+ mempool_t *, mempool_t *);
+
+request_queue_t *evms_find_queue(kdev_t dev);
+
+/* EVMS exported global variables */
+extern struct list_head evms_device_list;
+
+#endif
+

2002-10-10 20:09:01

by Kevin Corry

[permalink] [raw]
Subject: [PATCH] EVMS core (2/9) services.c

Greetings,

Part 2 of the EVMS core driver.

This file provides all of the common services that are available
to the EVMS plugins.

Kevin Corry
[email protected]
http://evms.sourceforge.net/



diff -Naur linux-2.5.41/drivers/evms/core/services.c linux-2.5.41-evms/drivers/evms/core/services.c
--- linux-2.5.41/drivers/evms/core/services.c Wed Dec 31 18:00:00 1969
+++ linux-2.5.41-evms/drivers/evms/core/services.c Thu Oct 10 13:14:27 2002
@@ -0,0 +1,1003 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2000 - 2002
+ *
+ * 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
+ */
+/*
+ * EVMS Common Services
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/bio.h>
+#include <linux/smp_lock.h>
+#include <asm/uaccess.h>
+
+#include <linux/evms.h>
+#include <linux/evms_ioctl.h>
+#include "evms_core.h"
+
+/*
+ * evms services version
+ */
+struct evms_version evms_svc_version = {
+ .major = EVMS_COMMON_SERVICES_MAJOR,
+ .minor = EVMS_COMMON_SERVICES_MINOR,
+ .patchlevel = EVMS_COMMON_SERVICES_PATCHLEVEL
+};
+
+/**
+ * evms_cs_check_version - compares to evms_version structures
+ * @required: required version level
+ * @actual: actual version level
+ *
+ * Compares two evms_version structures and returns -EINVAL if the
+ * actual version does not meet the required version level.
+ **/
+int
+evms_cs_check_version(struct evms_version *required,
+ struct evms_version *actual)
+{
+ if ((required->major != actual->major) ||
+ (required->minor > actual->minor) ||
+ ((required->minor == actual->minor) &&
+ (required->patchlevel > actual->patchlevel)))
+ return -EINVAL;
+ return 0;
+}
+
+EXPORT_SYMBOL(evms_cs_check_version);
+
+/**
+ * evms_cs_allocate_logical_node - allocates an evms logical node structure
+ * @pp: address of the pointer which will contain the address of newly allocated node
+ *
+ * Allocates and zeros an evms_logical_node structure.
+ *
+ * returns: 0 if sucessful
+ * -ENOMEM if unsuccessful
+ **/
+int
+evms_cs_allocate_logical_node(struct evms_logical_node **pp)
+{
+ *pp = kmalloc(sizeof (struct evms_logical_node), GFP_KERNEL);
+ if (*pp == NULL) {
+ return -ENOMEM;
+ }
+ memset(*pp, 0, sizeof (struct evms_logical_node));
+ INIT_LIST_HEAD(&(*pp)->discover);
+ INIT_LIST_HEAD(&(*pp)->device);
+ INIT_LIST_HEAD(&(*pp)->fbottom);
+ INIT_LIST_HEAD(&(*pp)->removable);
+ INIT_LIST_HEAD(&(*pp)->consumed);
+ INIT_LIST_HEAD(&(*pp)->produced);
+ return 0;
+}
+
+EXPORT_SYMBOL(evms_cs_allocate_logical_node);
+
+/**
+ * evms_cs_deallocate_volume_info - frees any attached volume info structure
+ * @p: address node to be freed
+ *
+ * If there is a evms_volume_info structure attached to this logical node, we
+ * know this node is a bottom-most storage object containing EVMS metadata for
+ * this volume. As such we must remove the node from the global feature node
+ * list, and then free the volume info structure.
+ **/
+void
+evms_cs_deallocate_volume_info(struct evms_logical_node *p)
+{
+ if (p->iflags & EVMS_FEATURE_BOTTOM) {
+ list_del_init(&p->fbottom);
+ kfree(p->volume_info);
+ p->volume_info = NULL;
+ p->iflags &= ~EVMS_FEATURE_BOTTOM;
+ }
+}
+
+EXPORT_SYMBOL(evms_cs_deallocate_volume_info);
+
+/**
+ * evms_cs_deallocate_logical_node - frees an evms logical node structure
+ * @p: address of node to be freed
+ *
+ * Frees an allocated logical node, frees any attached volume info or feature
+ * header structures.
+ **/
+void
+evms_cs_deallocate_logical_node(struct evms_logical_node *p)
+{
+ evms_cs_deallocate_volume_info(p);
+ if (p->feature_header) {
+ kfree(p->feature_header);
+ p->feature_header = NULL;
+ }
+ BUG_ON(!list_empty(&p->discover));
+ BUG_ON(!list_empty(&p->device));
+ BUG_ON(!list_empty(&p->fbottom));
+ BUG_ON(!list_empty(&p->removable));
+ BUG_ON(!list_empty(&p->consumed));
+ BUG_ON(!list_empty(&p->produced));
+ kfree(p);
+}
+
+EXPORT_SYMBOL(evms_cs_deallocate_logical_node);
+
+/**
+ * evms_cs_register_plugin - validates and registers a newly loaded kernel plugin
+ * @plugin: header of plugin attempting to register
+ *
+ * Validates and registers a new plugin.
+ **/
+int
+evms_cs_register_plugin(struct evms_plugin_header *plugin)
+{
+ int rc;
+ struct evms_plugin_header *p;
+
+ if (!(plugin_head.next && plugin_head.prev)) {
+ INIT_LIST_HEAD(&plugin_head);
+ }
+
+ LOG_EXTRA("registering plugin (id=%d.%d.%d, ver=%d.%d.%d)\n",
+ GetPluginOEM(plugin->id), GetPluginType(plugin->id),
+ GetPluginID(plugin->id), plugin->version.major,
+ plugin->version.minor, plugin->version.patchlevel);
+
+ /* ensure a plugin with this feature id is
+ * not already loaded.
+ */
+ spin_lock(&plugin_lock);
+ list_for_each_entry(p, &plugin_head, headers) {
+ if (p->id == plugin->id) {
+ spin_unlock(&plugin_lock);
+ rc = -EBUSY;
+ LOG_ERROR("error(%d) attempting to load another "
+ "plugin with id(%x).\n", rc, plugin->id);
+ return rc;
+ }
+ }
+ spin_unlock(&plugin_lock);
+
+ /* ensure the plugin has provided functions for
+ * the mandatory entry points.
+ */
+ if (!plugin->fops->discover ||
+ !plugin->fops->sync_io ||
+ !plugin->fops->ioctl ||
+ !plugin->fops->submit_io ||
+ !plugin->fops->open ||
+ !plugin->fops->release ||
+ !plugin->fops->check_media_change ||
+ !plugin->fops->revalidate ||
+ !plugin->fops->quiesce ||
+ !plugin->fops->get_geo ||
+ !plugin->fops->device_list ||
+ !plugin->fops->device_status ||
+ !plugin->fops->delete) {
+ return -EINVAL;
+ }
+ spin_lock(&plugin_lock);
+ list_add(&plugin->headers, &plugin_head);
+ spin_unlock(&plugin_lock);
+ return 0;
+}
+
+EXPORT_SYMBOL(evms_cs_register_plugin);
+
+/**
+ * evms_cs_unregister_plugin - unregisters a kernel plugin
+ * @plugin: header of plugin to unregister
+ *
+ * Unregisters a loaded EVMS kernel plugin.
+ **/
+int
+evms_cs_unregister_plugin(struct evms_plugin_header *plugin)
+{
+ struct evms_plugin_header *p;
+
+ LOG_EXTRA("unregistering plugin (id=%d.%d.%d ver=%d.%d.%d)\n",
+ GetPluginOEM(plugin->id), GetPluginType(plugin->id),
+ GetPluginID(plugin->id), plugin->version.major,
+ plugin->version.minor, plugin->version.patchlevel);
+
+ /* Ensure a plugin with this feature id is
+ * currently loaded.
+ */
+ spin_lock(&plugin_lock);
+ list_for_each_entry(p, &plugin_head, headers) {
+ if (p->id == plugin->id) {
+ list_del_init(&plugin->headers);
+ spin_unlock(&plugin_lock);
+ return 0;
+ }
+ }
+ spin_unlock(&plugin_lock);
+ LOG_ERROR("error(%d) attempt to unload a non-loaded plugin "
+ "with id(%x).\n", -ENOPKG, plugin->id);
+ return -ENOPKG;
+}
+
+EXPORT_SYMBOL(evms_cs_unregister_plugin);
+
+/**
+ * evms_cs_kernel_ioctl - performs a userspace ioctl from within the kernel
+ * @node: the storage object that is the target of this ioctl
+ * @cmd: the ioctl command
+ * @arg: the ioctl argument(s)
+ *
+ * Performs a userspace ioctl from within the kernel
+ **/
+int
+evms_cs_kernel_ioctl(struct evms_logical_node *node,
+ unsigned int cmd, ulong arg)
+{
+ int rc;
+ struct inode tmp_inode;
+ mm_segment_t fs;
+
+ lock_kernel();
+ fs = get_fs();
+ set_fs(get_ds());
+ rc = IOCTL(node, &tmp_inode, NULL, cmd, arg);
+ set_fs(fs);
+ unlock_kernel();
+
+ return rc;
+}
+
+EXPORT_SYMBOL(evms_cs_kernel_ioctl);
+
+/**
+ * evms_cs_size_in_vsectors - returns rounded-up 512 byte unit value
+ * *item_size: size of item in bytes
+ *
+ * Returns the # of 512 byte multiples an item occupies
+ **/
+inline ulong
+evms_cs_size_in_vsectors(long long item_size)
+{
+ long long sectors;
+ sectors = item_size >> EVMS_VSECTOR_SIZE_SHIFT;
+ if (item_size & (EVMS_VSECTOR_SIZE - 1))
+ sectors++;
+ return sectors;
+}
+
+EXPORT_SYMBOL(evms_cs_size_in_vsectors);
+
+/**
+ * evms_cs_log2 - computes the power of 2 of a specified value
+ * @value: any value
+ *
+ * returns: -1 for value of 0
+ * -2 if value is not a power of 2
+ * power of 2
+ **/
+inline int
+evms_cs_log2(long long value)
+{
+ int result = -1;
+ long long tmp;
+
+ if (value) {
+ tmp = value;
+ result++;
+ while (!(tmp & 1)) {
+ result++;
+ tmp >>= 1;
+ }
+ if (tmp != 1) {
+ result = -2;
+ }
+ }
+ return result;
+}
+
+EXPORT_SYMBOL(evms_cs_log2);
+
+/*
+ * Defines and variables used by the CRC function
+ */
+#define CRC_POLYNOMIAL 0xEDB88320L
+static u32 crc_table[256];
+static u32 crc_table_built = 0;
+
+/**
+ * build_crc_table
+ *
+ * Initialzes the internal crc table
+ **/
+static void
+build_crc_table(void)
+{
+ u32 i, j, crc;
+
+ for (i = 0; i <= 255; i++) {
+ crc = i;
+ for (j = 8; j > 0; j--) {
+ if (crc & 1)
+ crc = (crc >> 1) ^ CRC_POLYNOMIAL;
+ else
+ crc >>= 1;
+ }
+ crc_table[i] = crc;
+ }
+ crc_table_built = 1;
+}
+
+/**
+ * evms_cs_calculate_crc
+ * @crc: the inital(0xFFFFFFFF) or rolling crc value
+ * @buffer: address of buffer to compute crc
+ * @buffersize: size of buffer
+ *
+ * This function calculates the crc value for the data
+ * in the buffer specified by Buffer.
+ **/
+u32
+evms_cs_calculate_crc(u32 crc, void *buffer, u32 buffersize)
+{
+ unsigned char *current_byte;
+ u32 temp1, temp2, i;
+
+ current_byte = (unsigned char *) buffer;
+ /* Make sure the crc table is available */
+ if (crc_table_built == 0)
+ build_crc_table();
+ /* Process each byte in the buffer. */
+ for (i = 0; i < buffersize; i++) {
+ temp1 = (crc >> 8) & 0x00FFFFFF;
+ temp2 = crc_table[(crc ^ (u32) * current_byte) & (u32) 0xff];
+ current_byte++;
+ crc = temp1 ^ temp2;
+ }
+ return crc;
+}
+
+EXPORT_SYMBOL(evms_cs_calculate_crc);
+
+/**
+ * evms_end_io
+ * @bio: newly IO completed bio
+ *
+ * This is a support function for evms_cs_register_for_end_io_notification.
+ * This function is called during I/O completion on any bio that had its
+ * completion callback hooked by a plugin. Control is passed here
+ * and this routine will, thru the use of the I/O notify entry stored
+ * in the bi_private field of the bio, restore the bi_rsector value and
+ * the bi_bdev value to the value bio had at the time of hook registration
+ * and passes control to the registered callback_function, with pointers
+ * to the bio and an optional plugin private data. Upon completion of the
+ * callback_function, control is returned back here. The io notify list
+ * entry is deleted. This process repeats until this routine detects that
+ * all I/O notify entries registered by plugins have been called back and
+ * the bio's original end_io function has been called. At this point the
+ * DONE flag is set, and we terminate the callback loop and exit.
+ *
+ * Plugins may desire to break or interrupt the callback sequence or chain.
+ * This may be useful to redrive I/O or to wait for other bios to complete
+ * before allowing the original bio callback to occur. To interrupt the
+ * callback "chain", a registered plugin's callback_function must return
+ * with the DONE flag set.
+ *
+ * NOTE: If a plugin sets the DONE flag, and wishes to redrive a bio, the
+ * plugin MUST reregister the bio to receive another callback on this bio.
+ * Also, the plugin MUST ensure that the original bio end_io function gets
+ * called at some point, either by reregistering this bio and receiving
+ * another callback, or by means of bio aggregation triggered by the callbacks
+ * of other bios.
+ */
+static int
+evms_end_io(struct bio *bio,
+ unsigned int bytes_done,
+ int err)
+{
+ struct evms_io_notify *entry;
+ int done;
+
+ if (bio->bi_size)
+ return 1;
+
+ done = 0;
+ while (!done) {
+ entry = (struct evms_io_notify *) bio->bi_private;
+ /*
+ * restore original values
+ */
+ bio->bi_private = entry->b_private;
+ bio->bi_bdev = entry->bdev;
+ bio->bi_sector = entry->rsector;
+
+ if (entry->flags & EVMS_ORIGINAL_CALLBACK_FLAG) {
+ struct evms_logical_volume *lv;
+ lv = lookup_volume(minor(to_kdev_t(bio->bi_bdev->bd_dev)));
+ entry->flags &= ~EVMS_ORIGINAL_CALLBACK_FLAG;
+ if (atomic_dec_and_test(&lv->requests_in_progress) &&
+ lv->quiesced) {
+ wake_up(&lv->quiesce_wait_queue);
+ }
+ bio->bi_end_io = (void *) entry->callback_function;
+ if (bio->bi_end_io) {
+ bio_endio(bio, bytes_done, err);
+ }
+ done = 1;
+ } else {
+ entry->callback_function(entry->private, bio, &done);
+ }
+ mempool_free(entry, evms_io_notify_pool);
+ }
+
+ return 0;
+}
+
+/**
+ * evms_cs_register_for_end_io_notification
+ * @private: plugin private data
+ * @bio: bio being hooked/registered for
+ * @callback_function: plugin's callback function
+ *
+ * This routine allows a (plugin) function to register to participate
+ * in the io completion notification process. This is useful for plugins
+ * which alter data after it has been read from the disk (i.e.
+ * encryption or compression).
+ *
+ * This routine also records the rsector and bdev values at the time of
+ * registration, so that they can be restored prior to the callback to
+ * a plugin, thus allowing that plugin to work with the values it had seen
+ * during its I/O request processing.
+ *
+ * This routine also records a private data pointer at the time of
+ * registration that is returned to the plugin at callback time. This
+ * private data pointer frees the plugin from having to create a place to
+ * store private data and later find that private data at the time of the
+ * callback. This field is not used by this function and is optional (NULL
+ * if unused). It is recorded and returned as a convenience for the plugins.
+ *
+ * DANGER!!! - WILL ROBINSON - DANGER!!!
+ * This routine uses the bi_private field in the bio structure. If any lower
+ * level driver uses this field and do NOT save and restore it, the I/O
+ * callback will fail!!
+ */
+int
+evms_cs_register_for_end_io_notification(void *private,
+ struct bio *bio,
+ void *callback_function)
+{
+ int done;
+ struct evms_io_notify *new_entry;
+
+ done = 0;
+ while (!done) {
+ new_entry = mempool_alloc(evms_io_notify_pool, GFP_NOIO);
+ if (!new_entry) {
+ schedule();
+ continue;
+ }
+
+ new_entry->private = private;
+ new_entry->bio = bio;
+ new_entry->rsector = bio->bi_sector;
+ new_entry->bdev = bio->bi_bdev;
+ new_entry->b_private = bio->bi_private;
+ new_entry->flags = 0;
+
+ if (bio->bi_end_io != evms_end_io) {
+ struct evms_logical_volume *lv;
+ new_entry->flags |= EVMS_ORIGINAL_CALLBACK_FLAG;
+ lv = lookup_volume(minor(to_kdev_t(bio->bi_bdev->bd_dev)));
+ new_entry->callback_function = (void *) bio->bi_end_io;
+ atomic_inc(&lv->requests_in_progress);
+ bio->bi_end_io = evms_end_io;
+ } else {
+ new_entry->callback_function = callback_function;
+ done = 1;
+ }
+ bio->bi_private = new_entry;
+ }
+ return 0;
+}
+
+EXPORT_SYMBOL(evms_cs_register_for_end_io_notification);
+
+/**
+ * evms_cs_signal_event - notify a PID of an event occurrence
+ * @eventid: id of event to signal occurrence of
+ *
+ * Signal any registered PIDs that event corresponding to eventid has occurred.
+ **/
+void
+evms_cs_signal_event(int eventid)
+{
+ int rc;
+ struct evms_kevent *kevent;
+
+ /* signal PID(s) of specified event */
+ list_for_each_entry(kevent, &evms_notify_list, list) {
+ struct evms_event *event = &kevent->uevent;
+ if (event->eventid == eventid) {
+ struct task_struct *tsk;
+
+ tsk = find_task_by_pid(event->pid);
+ if (tsk) {
+ struct siginfo siginfo;
+
+ siginfo.si_signo = event->signo;
+ siginfo.si_errno = 0;
+ siginfo.si_code = 0;
+ rc = send_sig_info(event->signo, &siginfo, tsk);
+ } else {
+ /* TODO:
+ * unregister this stale
+ * notification record
+ */
+ }
+ }
+ }
+}
+
+EXPORT_SYMBOL(evms_cs_signal_event);
+
+/**
+ * evms_flush_signals
+ *
+ * Flushes pending signals for the current process
+ **/
+static inline void
+evms_flush_signals(void)
+{
+ spin_lock(&current->sig->siglock);
+ flush_signals(current);
+ spin_unlock(&current->sig->siglock);
+}
+
+/**
+ * evms_init_signals
+ *
+ * Initialize the signal set for the current process
+ **/
+static inline void
+evms_init_signals(void)
+{
+ current->exit_signal = SIGCHLD;
+ siginitsetinv(&current->blocked, sigmask(SIGKILL));
+}
+
+/**
+ * evms_kernel_thread - generic evms kernel thread templace
+ * @arg: thread structure
+ *
+ * Generic evms kernel thread template
+ **/
+static int
+evms_kernel_thread(void *arg)
+{
+ struct evms_thread *thread = arg;
+ lock_kernel();
+
+ /* Detach thread */
+ daemonize();
+
+ sprintf(current->comm, thread->name);
+ evms_init_signals();
+ evms_flush_signals();
+ thread->tsk = current;
+
+ unlock_kernel();
+
+ complete(thread->event);
+ while (thread->run) {
+ void (*run) (void *data);
+ DECLARE_WAITQUEUE(wait, current);
+
+ add_wait_queue(&thread->wqueue, &wait);
+ set_task_state(current, TASK_INTERRUPTIBLE);
+ if (!test_bit(EVMS_THREAD_WAKEUP, &thread->flags)) {
+ schedule();
+ }
+ current->state = TASK_RUNNING;
+ remove_wait_queue(&thread->wqueue, &wait);
+ clear_bit(EVMS_THREAD_WAKEUP, &thread->flags);
+
+ run = thread->run;
+ if (run) {
+ run(thread->data);
+ blk_run_queues();
+ }
+ if (signal_pending(current)) {
+ evms_flush_signals();
+ }
+ }
+ complete(thread->event);
+ return 0;
+}
+
+/**
+ * evms_cs_register_thread
+ * @run: function to be run by the kernel thread
+ * @data: argument for function
+ * @name: name for kernel thread
+ *
+ * Creates a kernel thread named @name, that invokes the function @run
+ * which accepts the argument @data.
+ *
+ * returns: ptr to thread control structure on success
+ * NULL on error
+ **/
+struct evms_thread *
+evms_cs_register_thread(void (*run) (void *), void *data, const u8 * name)
+{
+ struct evms_thread *thread;
+ int ret;
+ struct completion event;
+
+ thread = kmalloc(sizeof (struct evms_thread), GFP_KERNEL);
+ if (!thread)
+ return NULL;
+
+ memset(thread, 0, sizeof (struct evms_thread));
+ init_waitqueue_head(&thread->wqueue);
+
+ init_completion(&event);
+ thread->event = &event;
+ thread->run = run;
+ thread->data = data;
+ thread->name = name;
+ ret = kernel_thread(evms_kernel_thread, thread, 0);
+ if (ret < 0) {
+ kfree(thread);
+ return NULL;
+ }
+ wait_for_completion(&event);
+ return thread;
+}
+
+EXPORT_SYMBOL(evms_cs_register_thread);
+
+/**
+ * evms_cs_unregister_thread
+ * @thread: thread control structure (created when thread was registered)
+ *
+ * Destroys the specified kernel thread.
+ **/
+void
+evms_cs_unregister_thread(struct evms_thread *thread)
+{
+ struct completion event;
+
+ init_completion(&event);
+
+ thread->event = &event;
+ thread->run = NULL;
+ thread->name = NULL;
+ evms_cs_interrupt_thread(thread);
+ wait_for_completion(&event);
+ kfree(thread);
+}
+
+EXPORT_SYMBOL(evms_cs_unregister_thread);
+
+/**
+ * evms_cs_wakeup_thread
+ * @thread: thread control structure of thread to wake up
+ *
+ * Causes the kernel thread to wake up and run.
+ **/
+void
+evms_cs_wakeup_thread(struct evms_thread *thread)
+{
+ set_bit(EVMS_THREAD_WAKEUP, &thread->flags);
+ wake_up(&thread->wqueue);
+}
+
+EXPORT_SYMBOL(evms_cs_wakeup_thread);
+
+/**
+ * evms_cs_interrupt_thread
+ * @thread: thread control structure of thread to interrupt
+ *
+ * Signals a kernel thread.
+ **/
+void
+evms_cs_interrupt_thread(struct evms_thread *thread)
+{
+ if (!thread->tsk) {
+ LOG_ERROR("error: attempted to interrupt an invalid thread!\n");
+ return;
+ }
+ send_sig(SIGKILL, thread->tsk, 1);
+}
+
+EXPORT_SYMBOL(evms_cs_interrupt_thread);
+
+/**
+ * evms_cs_get_evms_proc_dir
+ *
+ * Retrieves the EVMS proc dir entry.
+ **/
+struct proc_dir_entry *
+evms_cs_get_evms_proc_dir(void)
+{
+ if (!evms_proc_dir) {
+ evms_proc_dir = create_proc_entry("evms", S_IFDIR, &proc_root);
+ }
+ return evms_proc_dir;
+}
+
+EXPORT_SYMBOL(evms_cs_get_evms_proc_dir);
+
+/**
+ * evms_cs_volume_request_in_progress - get/change a volume's request in progress count
+ * @dev:
+ * @operation: input, > 0 = inc count, < 0 = dec count, 0 = query count
+ * @current_count: output, current count
+ *
+ * Query or change a volume's request in progress count.
+ *
+ * returns: 0 = success
+ * -ENODEV if dev points to a non-existent volume
+ **/
+int
+evms_cs_volume_request_in_progress(kdev_t dev,
+ int operation, int *current_count)
+{
+ struct evms_logical_volume *volume = lookup_volume(minor(dev));
+ if (!volume || !volume->node) {
+ return -ENODEV;
+ }
+ if (operation > 0) {
+ atomic_inc(&volume->requests_in_progress);
+ } else if (operation < 0) {
+ if (atomic_dec_and_test(&volume->requests_in_progress) &&
+ volume->quiesced) {
+ wake_up(&volume->quiesce_wait_queue);
+ }
+ }
+ if (current_count) {
+ *current_count = atomic_read(&volume->requests_in_progress);
+ }
+ return 0;
+}
+
+EXPORT_SYMBOL(evms_cs_volume_request_in_progress);
+
+/**
+ * evms_cs_invalidate_volume - invalidate a volume's cache blocks
+ * @node: top-most storage object in a volume
+ *
+ * Invalidates the resources (inodes, cache blocks, etc) used by this volume.
+ **/
+void
+evms_cs_invalidate_volume(struct evms_logical_node *node)
+{
+ struct evms_logical_volume *lv = NULL;
+ while ((lv = find_next_volume(lv))) {
+ if (!lv->node || !node->name) {
+ continue;
+ }
+ if (strcmp(lv->node->name, node->name)) {
+ continue;
+ }
+ LOG_DETAILS("Invalidating EVMS device %s minor %d\n",
+ node->name, lv->minor);
+ invalidate_device(mk_kdev(EVMS_MAJOR, lv->minor), 0);
+ break;
+ }
+}
+
+EXPORT_SYMBOL(evms_cs_invalidate_volume);
+
+/**
+ * evms_bio_collector - manages split bio and real bio I/O completions
+ * @bio: a split bio
+ *
+ * Collects all the split bio end_io's and when all have completed, calls
+ * the original bio's end_io, and setting the uptodate flag accordingly.
+ **/
+int
+evms_bio_collector(struct bio *bio,
+ unsigned int bytes_done,
+ int err)
+{
+ struct bio_split_cb *evms_bio_split_record;
+
+ if (bio->bi_size)
+ return 1;
+
+ evms_bio_split_record = (struct bio_split_cb *) bio->bi_private;
+ if (err || !test_bit(BIO_UPTODATE, &bio->bi_flags)) {
+ evms_bio_split_record->rc = -EIO;
+ }
+ mempool_free(bio, evms_bio_split_record->bio_pool);
+ atomic_dec(&evms_bio_split_record->outstanding_bios);
+ if (!atomic_read(&evms_bio_split_record->outstanding_bios)) {
+ int rc = evms_bio_split_record->rc;
+ bio = evms_bio_split_record->original_bio;
+ if (!rc) {
+ set_bit(BIO_UPTODATE, &bio->bi_flags);
+ }
+ mempool_free(evms_bio_split_record, evms_bio_split_record->split_pool);
+ bio_endio(bio, bio->bi_size, rc);
+ }
+
+ return 0;
+}
+
+/**
+ * Convenience macro for initializing the bio split control block.
+ **/
+#define EVMS_SPLIT_CB_INIT(bio, split_rec, b_pool, s_pool) \
+ (split_rec)->rc = 0; \
+ (split_rec)->outstanding_bios = (atomic_t)ATOMIC_INIT(1); \
+ (split_rec)->original_bio = bio; \
+ (split_rec)->bio_pool = b_pool; \
+ (split_rec)->split_pool = s_pool;
+
+/**
+ * Convenience macro for initializing a new bio.
+ **/
+#define EVMS_BIO_INIT(bio, split_rec) \
+ bio_init(bio); \
+ (bio)->bi_end_io = evms_bio_collector; \
+ (bio)->bi_private = (split_rec);
+
+/**
+ * evms_bio_vcnt - computes the correct number of bio_vectors needed for a piece of storage
+ * @addr: virtual address of storage
+ * @size: size of storage
+ *
+ * Computes the correct number of required bio vectors, with respect to page
+ * size and alignment, for given a memory object starting address and size.
+ *
+ * returns: the count of bio vectors required.
+ **/
+static inline int
+evms_bio_vcnt(ulong addr, int size)
+{
+ int vcnt, offset, lskew, rskew;
+
+ vcnt = (size + PAGE_SIZE - 1) >> PAGE_SHIFT;
+ rskew = size & (PAGE_SIZE - 1);
+ offset = addr & (PAGE_SIZE - 1);
+ lskew = PAGE_SIZE - offset;
+ if (lskew < rskew) {
+ vcnt++;
+ }
+ return vcnt;
+}
+
+/**
+ * evms_cs_split_bio - split a bio into two smaller bios.
+ * @source_bio: the original bio
+ * @target_size: the size of the target bio
+ * @target_bio: the resulting target bio
+ * @remainder_bio: the bio of the remaining data
+ *
+ * Split the source bio into a target bio, of a specified size, and remainder
+ * bio of the remaining size. The caller can then submit the target bio and if
+ * necessary split the remaining bio down futher with subsequent calls to this
+ * function. This function handles allocating and initializing the target bio
+ * & remainder bios as well as the bio split control block. All split bio's end
+ * io will pass control to the bio collector function which will collect the
+ * end ios until all the splits have completed and will then call the original
+ * bio's end io.
+ *
+ * returns: 0 on sucess, a target bio, a remainder bio
+ * error code on error
+ **/
+int
+evms_cs_split_bio(struct bio *source_bio,
+ u64 target_size,
+ struct bio **target_bio, struct bio **remainder_bio,
+ mempool_t *my_bio_pool, mempool_t *my_bio_split_pool)
+{
+ int rc = 0;
+ struct bio_split_cb *evms_bio_split_record = NULL;
+
+ /*
+ * validate split target_size
+ */
+ if (target_size >= source_bio->bi_size) {
+ rc = -EINVAL;
+ LOG_ERROR("error(%d): unable to split bio(size:%d) "
+ "on specified boundary("PFU64").\n",
+ rc, source_bio->bi_size, target_size);
+ return rc;
+ }
+
+ /*
+ * allocate target bio and bio split record if needed
+ */
+ if (source_bio->bi_end_io != evms_bio_collector) {
+ *target_bio = mempool_alloc(my_bio_pool, GFP_NOIO);
+ evms_bio_split_record =
+ mempool_alloc(my_bio_split_pool, GFP_NOIO);
+ EVMS_SPLIT_CB_INIT(source_bio, evms_bio_split_record,
+ my_bio_pool, my_bio_split_pool);
+ EVMS_BIO_INIT(*target_bio, evms_bio_split_record);
+ (*target_bio)->bi_rw = source_bio->bi_rw;
+ (*target_bio)->bi_bdev = source_bio->bi_bdev;
+ } else {
+ *target_bio = source_bio;
+ evms_bio_split_record =
+ (struct bio_split_cb *) source_bio->bi_private;
+ }
+
+ /*
+ * allocate remainder bio
+ */
+ *remainder_bio = mempool_alloc(my_bio_pool, GFP_NOIO);
+ atomic_inc(&evms_bio_split_record->outstanding_bios);
+ EVMS_BIO_INIT(*remainder_bio, evms_bio_split_record);
+ (*remainder_bio)->bi_rw = source_bio->bi_rw;
+ (*remainder_bio)->bi_bdev = source_bio->bi_bdev;
+
+ /*
+ * build the io vecs for target and remainder
+ */
+ {
+ int remaining_bytes, i, src_vcnt, cur_vcnt;
+ struct bio_vec *src_io_vec, *cur_io_vec;
+
+ remaining_bytes = target_size;
+ src_io_vec = &source_bio->bi_io_vec[0];
+ cur_io_vec = &(*target_bio)->bi_io_vec[0];
+ src_vcnt = source_bio->bi_vcnt;
+ for (i = cur_vcnt = 0; i < src_vcnt; i++, src_io_vec++) {
+ unsigned int src_len = src_io_vec->bv_len;
+
+ cur_io_vec->bv_offset = src_io_vec->bv_offset;
+ cur_io_vec->bv_page = src_io_vec->bv_page;
+ if ((remaining_bytes <= 0)
+ || (remaining_bytes > src_len)) {
+ cur_io_vec->bv_len = src_len;
+ } else {
+ unsigned int trg_len = remaining_bytes;
+
+ cur_io_vec->bv_len = remaining_bytes;
+ cur_vcnt++;
+ (*target_bio)->bi_vcnt = cur_vcnt;
+
+ cur_vcnt = remaining_bytes = 0;
+ cur_io_vec = &(*remainder_bio)->bi_io_vec[0];
+ if (!(src_len - trg_len))
+ continue;
+ cur_io_vec->bv_len = src_len - trg_len;
+ cur_io_vec->bv_offset =
+ src_io_vec->bv_offset + trg_len;
+ cur_io_vec->bv_page = src_io_vec->bv_page;
+ }
+ cur_vcnt++;
+ remaining_bytes -= cur_io_vec->bv_len;
+ cur_io_vec++;
+ }
+ (*remainder_bio)->bi_vcnt = cur_vcnt;
+ }
+ /*
+ * update the sector and size fields in the
+ * target and remainder bio.
+ */
+ (*remainder_bio)->bi_size = source_bio->bi_size - target_size;
+ (*remainder_bio)->bi_sector = source_bio->bi_sector;
+ (*remainder_bio)->bi_sector += target_size >> EVMS_VSECTOR_SIZE_SHIFT;
+ (*target_bio)->bi_size = target_size;
+ (*target_bio)->bi_sector = source_bio->bi_sector;
+
+ return rc;
+}
+
+EXPORT_SYMBOL(evms_cs_split_bio);
+

2002-10-10 20:21:48

by Kevin Corry

[permalink] [raw]
Subject: [PATCH] EVMS core (7/9) evms_ioctl.h

Greetings,

Part 7 of the EVMS core driver.

This is another public header file for EVMS, which contains the
ioctl interface definitions.

Kevin Corry
[email protected]
http://evms.sourceforge.net/



diff -Naur linux-2.5.41/include/linux/evms_ioctl.h linux-2.5.41-evms/include/linux/evms_ioctl.h
--- linux-2.5.41/include/linux/evms_ioctl.h Wed Dec 31 18:00:00 1969
+++ linux-2.5.41-evms/include/linux/evms_ioctl.h Thu Oct 10 11:20:28 2002
@@ -0,0 +1,516 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2000 - 2002
+ *
+ * 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
+ */
+/*
+ * EVMS kernel ioctl header file
+ *
+ * This file contains all information about the ioctl interface.
+ */
+
+#ifndef __EVMS_IOCTL_INCLUDED__
+#define __EVMS_IOCTL_INCLUDED__
+
+/* IOCTL interface version definitions */
+#define EVMS_IOCTL_INTERFACE_MAJOR 12
+#define EVMS_IOCTL_INTERFACE_MINOR 0
+#define EVMS_IOCTL_INTERFACE_PATCHLEVEL 0
+
+/* IOCTL definitions */
+enum evms_ioctl_cmds {
+ /* version commands */
+ EVMS_GET_IOCTL_VERSION_NUMBER = 0,
+ EVMS_GET_VERSION_NUMBER,
+
+ /* EVMS internal commands */
+ EVMS_GET_DISK_LIST_NUMBER = 0x40,
+ EVMS_CHECK_MEDIA_CHANGE_NUMBER,
+ EVMS_REVALIDATE_DISK_NUMBER,
+ EVMS_OPEN_VOLUME_NUMBER,
+ EVMS_CLOSE_VOLUME_NUMBER,
+ EVMS_QUIESCE_VOLUME_NUMBER,
+ EVMS_CHECK_DEVICE_STATUS_NUMBER,
+ EVMS_UPDATE_DEVICE_INFO_NUMBER,
+
+ /* configuration commands */
+ EVMS_GET_INFO_LEVEL_NUMBER = 0x80,
+ EVMS_SET_INFO_LEVEL_NUMBER,
+ EVMS_REDISCOVER_VOLUMES_NUMBER,
+ EVMS_DELETE_VOLUME_NUMBER,
+ EVMS_PLUGIN_IOCTL_NUMBER,
+ EVMS_PROCESS_NOTIFY_EVENT_NUMBER,
+
+ /* query info commands */
+ EVMS_GET_LOGICAL_DISK_NUMBER = 0xC0,
+ EVMS_GET_LOGICAL_DISK_INFO_NUMBER,
+ EVMS_SECTOR_IO_NUMBER,
+ EVMS_GET_MINOR_NUMBER,
+ EVMS_GET_VOLUME_DATA_NUMBER,
+ EVMS_GET_PLUGIN_NUMBER,
+ EVMS_COMPUTE_CSUM_NUMBER,
+ EVMS_GET_BMAP_NUMBER,
+ EVMS_CHECK_MOUNT_STATUS_NUMBER,
+ EVMS_CHECK_OPEN_STATUS_NUMBER,
+
+ /* commands for non-EVMS apps */
+ EVMS_GET_VOL_STRIPE_INFO_NUMBER = 0xF0,
+};
+
+/* version commands */
+#define EVMS_GET_IOCTL_VERSION _IOR(EVMS_MAJOR, EVMS_GET_IOCTL_VERSION_NUMBER, \
+ struct evms_version)
+#define EVMS_GET_VERSION _IOR(EVMS_MAJOR, EVMS_GET_VERSION_NUMBER, \
+ struct evms_version)
+
+/* EVMS internal commands */
+#define EVMS_GET_DISK_LIST _IOWR(EVMS_MAJOR, EVMS_GET_DISK_LIST_NUMBER, \
+ struct list_head *)
+#define EVMS_CHECK_MEDIA_CHANGE _IO(EVMS_MAJOR, EVMS_CHECK_MEDIA_CHANGE_NUMBER)
+#define EVMS_REVALIDATE_DISK _IO(EVMS_MAJOR, EVMS_REVALIDATE_DISK_NUMBER)
+#define EVMS_OPEN_VOLUME _IO(EVMS_MAJOR, EVMS_OPEN_VOLUME_NUMBER)
+#define EVMS_CLOSE_VOLUME _IO(EVMS_MAJOR, EVMS_CLOSE_VOLUME_NUMBER)
+
+/**
+ * struct evms_quiesce_vol_pkt - ioctl packet definition
+ * @command: 0 = unquiesce, 1 = quiesce
+ * @minor: minor device number of target volume
+ * @do_vfs: 0 = do nothing, 1 = also perform equivalent VFS operation
+ * @status: returned operation status
+ *
+ * ioctl packet definition for EVMS_QUIESCE_VOLUME
+ **/
+struct evms_quiesce_vol_pkt {
+ s32 command;
+ s32 minor;
+ s32 do_vfs;
+ s32 status;
+};
+/**
+ * defines for evms_quiesce_vol_pkt.command field
+ **/
+#define EVMS_UNQUIESCE 0
+#define EVMS_QUIESCE 1
+/**
+ * defines for evms_quiesce_vol_pkt.do_vfs field
+ * located below struct evms_delete_vol_pkt definition
+ **/
+
+#define EVMS_QUIESCE_VOLUME _IOR(EVMS_MAJOR, \
+ EVMS_QUIESCE_VOLUME_NUMBER, \
+ struct evms_quiesce_vol_pkt)
+#define EVMS_CHECK_DEVICE_STATUS _IOR(EVMS_MAJOR, \
+ EVMS_CHECK_DEVICE_STATUS_NUMBER, \
+ int)
+#define EVMS_UPDATE_DEVICE_INFO _IO(EVMS_MAJOR, \
+ EVMS_UPDATE_DEVICE_INFO_NUMBER)
+
+/* configuration commands */
+#define EVMS_GET_INFO_LEVEL _IOR(EVMS_MAJOR, \
+ EVMS_GET_INFO_LEVEL_NUMBER, int)
+#define EVMS_SET_INFO_LEVEL _IOW(EVMS_MAJOR, \
+ EVMS_SET_INFO_LEVEL_NUMBER, int)
+
+/**
+ * struct evms_rediscover_pkt - rediscover volume ioctl packet definition
+ * @status: return operation status
+ * @drive_count: count of drives being probed, 0xffffffff for all disks
+ * @drive_array: array of drive handles to be probed
+ *
+ * ioctl packet definition for EVMS_REDISCOVER_VOLUMES ioctl
+ **/
+struct evms_rediscover_pkt {
+ s32 status;
+ u32 drive_count;
+ u64 *drive_array;
+};
+/**
+ * defines for evms_delete_vol_pkt.command field
+ **/
+#define EVMS_SOFT_DELETE 0
+#define EVMS_HARD_DELETE 1
+/**
+ * defines evms_rediscover_pkt.drive_count field
+ **/
+#define REDISCOVER_ALL_DEVICES 0xFFFFFFFF
+
+#define EVMS_REDISCOVER_VOLUMES _IOWR(EVMS_MAJOR, EVMS_REDISCOVER_VOLUMES_NUMBER, \
+ struct evms_rediscover_pkt)
+
+/**
+ * struct evms_delete_vol_pkt - delete volume ioctl packet definition
+ * @command: 0 = soft delete, 1 = hard delete
+ * @minor: minor device num of target volume
+ * @do_vfs: 0 = do nothing, 1 = perform VFS operation(s)
+ * @associative_minor: optional minor device num of associative volume, 0 when unused
+ * @author returned operation status
+ *
+ * ioctl packet definition for EVMS_DELETE_VOLUME ioctl
+ **/
+struct evms_delete_vol_pkt {
+ s32 command;
+ s32 minor;
+ s32 do_vfs;
+ s32 associative_minor;
+ s32 status;
+};
+/**
+ * field evms_delete_vol_pkt defines
+ * @EVMS_VFS_DO_NOTHING:
+ * @EVMS_VFS_DO:
+ *
+ * NOTE: these defines are also used with evms_quiesce_vol_pkt.
+ **/
+#define EVMS_VFS_DO_NOTHING 0
+#define EVMS_VFS_DO 1
+
+#define EVMS_DELETE_VOLUME _IOR(EVMS_MAJOR, EVMS_DELETE_VOLUME_NUMBER, \
+ struct evms_delete_vol_pkt)
+
+/**
+ * struct evms_plugin_ioctl_pkt - generic plugin ioctl packet definition
+ * @feature_id: plugin ID of feature to receive this ioctl
+ * @feature_command: feature specific ioctl command
+ * @status: 0 = completed, 0 != error
+ * @feature_ioctl_data: ptr to feature specific ioctl struct
+ *
+ * ioctl packet definition for EVMS_PLUGIN_IOCTL ioctl
+ **/
+struct evms_plugin_ioctl_pkt {
+ u32 feature_id;
+ s32 feature_command;
+ s32 status;
+ u32 data_size;
+ void *feature_ioctl_data;
+};
+
+#define EVMS_PLUGIN_IOCTL _IOR(EVMS_MAJOR, EVMS_PLUGIN_IOCTL_NUMBER, \
+ struct evms_plugin_ioctl_pkt)
+
+/**
+ * struct evms_event - evms event structure
+ * @pid: PID to act on
+ * @eventid: event id to respond to
+ * @signo: signal # to send when event occurs
+ *
+ * contains process event notification info
+ **/
+struct evms_event {
+ s32 pid;
+ s32 eventid;
+ s32 signo;
+};
+/**
+ * field evms_event_pkt.eventid defines
+ **/
+#define EVMS_EVENT_END_OF_DISCOVERY 0
+
+/**
+ * struct evms_notify_pkt - evms event notification ioctl packet definition
+ * @command: 0 = unregister, 1 = register
+ * @eventry: event structure
+ * @status: returned operation status
+ *
+ * ioctl packet definition for EVMS_PROCESS_NOTIFY_EVENT ioctl
+ **/
+struct evms_notify_pkt {
+ s32 command;
+ s32 status;
+ struct evms_event eventry;
+};
+/**
+ * field evms_notify_pkt.command defines
+ **/
+#define EVMS_EVENT_UNREGISTER 0
+#define EVMS_EVENT_REGISTER 1
+
+#define EVMS_PROCESS_NOTIFY_EVENT _IOWR(EVMS_MAJOR, \
+ EVMS_PROCESS_NOTIFY_EVENT_NUMBER, \
+ struct evms_notify_pkt)
+
+/* query info commands */
+
+/**
+ * struct evms_user_disk_pkt - get disk handle ioctl packet definition
+ * @command: 0 = first disk, 1 = next disk
+ * @status: 0 = no more disks, 1 = valid disk info
+ * @disk_handle: only valid when status == 1
+ *
+ * ioctl packet definition for EVMS_GET_LOGICAL_DISK ioctl
+ **/
+struct evms_user_disk_pkt {
+ s32 command;
+ s32 status;
+ u64 disk_handle;
+};
+/**
+ * field evms_user_disk_pkt.command defines
+ **/
+#define EVMS_FIRST_DISK 0
+#define EVMS_NEXT_DISK 1
+/**
+ * field evms_user_disk_pkt.status defines
+ **/
+#define EVMS_DISK_INVALID 0
+#define EVMS_DISK_VALID 1
+
+#define EVMS_GET_LOGICAL_DISK _IOWR(EVMS_MAJOR, EVMS_GET_LOGICAL_DISK_NUMBER, \
+ struct evms_user_disk_pkt)
+
+/**
+ * evms_user_disk_info_pkt - disk info packet definition
+ * @disk_handle: kernel handle to specified device
+ * @total_sectors: size of device in 512 byte units
+ * @geo_cylinders: device geometry: cylinders
+ * @geo_sectors: device geometry: sectors
+ * @geo_heads: device geometry: heads
+ * @disk_dev: kernel device info, used by MD plugin
+ * @block_size: reported block size
+ * @hardsect_size: reported physical sector size
+ * @status: return operation status
+ * @flags: device characteristics
+ * @disk_name: legacy name for the device
+ *
+ * ioctl packet definition for EVMS_GET_LOGICAL_DISK_INFO ioctl
+ **/
+struct evms_user_disk_info_pkt {
+ u64 disk_handle;
+ u64 total_sectors;
+ u64 geo_cylinders;
+ u32 geo_sectors;
+ u32 geo_heads;
+ u32 disk_dev;
+ u32 block_size;
+ u32 hardsect_size;
+ u32 status;
+ u32 flags;
+ u8 disk_name[EVMS_VOLUME_NAME_SIZE + 1];
+};
+/**
+ * field evms_user_disk_info_pkt.flags define in evms.h
+ **/
+
+#define EVMS_GET_LOGICAL_DISK_INFO _IOWR(EVMS_MAJOR, \
+ EVMS_GET_LOGICAL_DISK_INFO_NUMBER, \
+ struct evms_user_disk_info_pkt)
+
+/**
+ * struct evms_sector_io_pkt - sector io ioctl packet definition
+ * @disk_handle: disk handle of target device
+ * @starting_sector: disk relative starting sector
+ * @sector_count: count of sectors
+ * @io_flag: 0 = read, 1 = write
+ * @status: return operation status
+ * @buffer_address: user buffer address
+ *
+ * ioctl packet definition for EVMS_SECTOR_IO ioctl
+ **/
+struct evms_sector_io_pkt {
+ u64 disk_handle;
+ u64 starting_sector;
+ u64 sector_count;
+ s32 io_flag;
+ s32 status;
+ u8 *buffer_address;
+};
+/**
+ * field evms_sector_io_pkt.io_flag defines
+ **/
+#define EVMS_SECTOR_IO_READ 0
+#define EVMS_SECTOR_IO_WRITE 1
+
+#define EVMS_SECTOR_IO _IOWR(EVMS_MAJOR, EVMS_SECTOR_IO_NUMBER, \
+ struct evms_sector_io_pkt)
+
+/**
+ * struct evms_user_minor_pkt - get a list of device minors, one at a time
+ * @command: 0 = first volume, 1 = next volume
+ * @status: returned operation status
+ * @minor: returned minor number, only valid when status == 1
+ *
+ * ioctl packet definition for EVMS_GET_MINOR ioctl
+ **/
+struct evms_user_minor_pkt {
+ s32 command;
+ s32 status;
+ s32 minor;
+};
+/**
+ * field evms_user_minor_pkt.command defines
+ **/
+#define EVMS_FIRST_VOLUME 0
+#define EVMS_NEXT_VOLUME 1
+/**
+ * field evms_user_minor_pkt.status defines
+ **/
+#define EVMS_VOLUME_INVALID 0
+#define EVMS_VOLUME_VALID 1
+
+#define EVMS_GET_MINOR _IOWR(EVMS_MAJOR, EVMS_GET_MINOR_NUMBER, \
+ struct evms_user_minor_pkt)
+
+/**
+ * struct evms_volume_data_pkt - volume data packet definition
+ * @minor: minor device number of target volume
+ * @flags: returned volume characteristics
+ * @volume_name: returned volume name
+ * @status: returned operation status
+ *
+ * ioctl packet definition for EVMS_GET_VOLUME_DATA ioctl
+ **/
+struct evms_volume_data_pkt {
+ s32 minor;
+ s32 flags;
+ s32 status;
+ u8 volume_name[EVMS_VOLUME_NAME_SIZE + 1];
+};
+/**
+ * field evms_volume_data_pkt.flags defines found in evms_common.h
+ **/
+
+#define EVMS_GET_VOLUME_DATA _IOWR(EVMS_MAJOR, EVMS_GET_VOLUME_DATA_NUMBER, \
+ struct evms_volume_data_pkt)
+
+/**
+ * struct evms_kernel_plugin_pkt - get kernel plugin ioctl packet definition
+ * @command: 0 = first plugin, 1 = next plugin
+ * @id: returned plugin id
+ * @version: returned plugin version info
+ * @status: returned operation status
+ *
+ * ioctl packet definition for EVMS_GET_PLUGIN ioctl
+ **/
+struct evms_kernel_plugin_pkt {
+ s32 command;
+ u32 id;
+ struct evms_version version;
+ s32 status;
+};
+/**
+ * field evms_kernel_plugin_pkt.command defines
+ **/
+#define EVMS_FIRST_PLUGIN 0
+#define EVMS_NEXT_PLUGIN 1
+/**
+ * field evms_kernel_plugin_pkt.status defines
+ **/
+#define EVMS_PLUGIN_INVALID 0
+#define EVMS_PLUGIN_VALID 1
+
+#define EVMS_GET_PLUGIN _IOWR(EVMS_MAJOR, EVMS_GET_PLUGIN_NUMBER, \
+ struct evms_kernel_plugin_pkt)
+
+/**
+ * struct evms_compute_csum_pkt - compute checksum ioctl packet definition
+ * @buffer_address:
+ * @buffer_size:
+ * @insum:
+ * @outsum:
+ * @status:
+ *
+ * ioctl packet definition for EVMS_COMPUTE_CSUM ioctl
+ **/
+struct evms_compute_csum_pkt {
+ u8 *buffer_address;
+ s32 buffer_size;
+ u32 insum;
+ u32 outsum;
+ s32 status;
+};
+
+#define EVMS_COMPUTE_CSUM _IOWR(EVMS_MAJOR, EVMS_COMPUTE_CSUM_NUMBER, \
+ struct evms_compute_csum_pkt)
+
+/**
+ * struct evms_get_bmap_pkt - get bmap data ioctl packet definition
+ * @rsector: input, volume relative rsector value
+ * output, disk relative rsector value
+ * @dev output, physical device
+ * @status: output, operation status
+ *
+ * ioctl packet definition for EVMS_GET_BMAP ioctl
+ **/
+struct evms_get_bmap_pkt {
+ u64 rsector;
+ u32 dev;
+ s32 status;
+};
+
+#define EVMS_GET_BMAP _IOWR(EVMS_MAJOR, EVMS_GET_BMAP_NUMBER, \
+ struct evms_get_bmap_pkt)
+
+/**
+ * struct evms_mount_status_pkt - ioctl packet definition
+ * @minor: input, minor of volume to check
+ * @mounted: output, TRUE (1) if mounted, FALSE (0) if not
+ * @status: output, operation completion status
+ *
+ * ioctl packet definition for EVMS_CHECK_MOUNT_STATUS ioctl.
+ **/
+struct evms_mount_status_pkt {
+ u32 minor;
+ u32 mounted;
+ s32 status;
+};
+
+#define EVMS_CHECK_MOUNT_STATUS _IOWR(EVMS_MAJOR, EVMS_CHECK_MOUNT_STATUS_NUMBER, \
+ struct evms_mount_status_pkt)
+
+/**
+ * struct evms_open_status_pkt - ioctl packet definition
+ * @minor: input, minor of volume to check
+ * @opens: output, 0 (FALSE) if not, count (TRUE) of opens
+ * @status: output, operation completion status
+ *
+ * ioctl packet definition for EVMS_CHECK_OPEN_STATUS ioctl.
+ **/
+struct evms_open_status_pkt {
+ u32 minor;
+ u32 opens;
+ s32 status;
+};
+
+#define EVMS_CHECK_OPEN_STATUS _IOWR(EVMS_MAJOR, EVMS_CHECK_OPEN_STATUS_NUMBER, \
+ struct evms_open_status_pkt)
+
+/**
+ * struct evms_vol_stripe_info_pkt - ioctl packet definition
+ * @size: the stripe unit specified in 512 byte block units
+ * @width: the number of stripe members or RAID data disks
+ *
+ * ioctl packet definition for EVMS_GET_VOL_STRIPE_INFO ioctl.
+ **/
+struct evms_vol_stripe_info_pkt {
+ u32 size;
+ u32 width;
+};
+
+#define EVMS_GET_VOL_STRIPE_INFO _IOR(EVMS_MAJOR, EVMS_GET_VOL_STRIPE_INFO_NUMBER, \
+ struct evms_vol_stripe_info_pkt)
+
+/* On 64-bit systems with 32-bit user space, declare a prototype to the
+ * ioctl conversion registration functions. On all other systems, define
+ * a static inline function to replace the call.
+ */
+#if defined(CONFIG_PPC64) || defined(CONFIG_SPARC64) || defined(CONFIG_X86_64)
+extern void evms_ioctl32_register(void);
+extern void evms_ioctl32_unregister(void);
+#else
+static inline void evms_ioctl32_register(void) { }
+static inline void evms_ioctl32_unregister(void) { }
+#endif
+
+#endif
+

2002-10-10 20:12:10

by Kevin Corry

[permalink] [raw]
Subject: [PATCH] EVMS core (1/9) evms.c

Greetings,

Part 1 of the EVMS core driver.

This file provides all of the block device requirements, the module
init/exit routines, and the ioctl interface to EVMS.

Kevin Corry
[email protected]
http://evms.sourceforge.net/



diff -Naur linux-2.5.41/drivers/evms/core/evms.c linux-2.5.41-evms/drivers/evms/core/evms.c
--- linux-2.5.41/drivers/evms/core/evms.c Sun Jul 17 18:46:18 1994
+++ linux-2.5.41-evms/drivers/evms/core/evms.c Thu Oct 10 13:50:01 2002
@@ -0,0 +1,2692 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2000 - 2002
+ *
+ * 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
+ */
+/*
+ * EVMS core driver.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/blk.h>
+#include <linux/blkpg.h>
+#include <linux/bio.h>
+#include <linux/hdreg.h>
+#include <linux/mempool.h>
+#include <linux/reboot.h>
+#include <asm/uaccess.h>
+#include <asm/checksum.h>
+
+#include <linux/evms.h>
+#include <linux/evms_ioctl.h>
+#include "evms_core.h"
+
+/**
+ * Global data
+ **/
+/*
+ * evms_info_level is the in-memory syslog level
+ */
+int evms_info_level = EVMS_INFO_LEVEL;
+EXPORT_SYMBOL(evms_info_level);
+struct proc_dir_entry *evms_proc_dir = NULL;
+
+/*
+ * the rediscover semaphore
+ */
+static struct semaphore red_sem;
+
+/*
+ * list of all registered (loaded) plugins
+ */
+struct list_head plugin_head;
+spinlock_t plugin_lock = SPIN_LOCK_UNLOCKED;
+
+/*
+ * list of all disk devices EVMS is aware of
+ */
+struct list_head evms_device_list;
+EXPORT_SYMBOL(evms_device_list);
+
+/*
+ * list of all bottom level storage objects with EVMS metadata
+ */
+struct list_head evms_fbottom_list;
+
+/*
+ * list of all registered notification events
+ */
+struct list_head evms_notify_list;
+
+struct list_head evms_logical_volumes;
+int evms_volumes = 0;
+
+/*
+ * internal POOL variables
+ */
+mempool_t *evms_io_notify_pool;
+static kmem_cache_t *evms_io_notify_slab;
+
+/*
+ * Handle for the devfs directory entry
+ */
+devfs_handle_t evms_dir_devfs_handle;
+devfs_handle_t evms_blk_devfs_handle;
+
+/**
+ * SYSCTL - EVMS folder definitions/variables
+ **/
+static struct ctl_table_header *evms_table_header;
+static int evms_info_level_min = EVMS_INFO_CRITICAL;
+static int evms_info_level_max = EVMS_INFO_EVERYTHING;
+
+static struct ctl_table evms_table[] = {
+ {DEV_EVMS_INFO_LEVEL, "evms_info_level",
+ &evms_info_level, sizeof (int), 0644, NULL,
+ &proc_dointvec_minmax, &sysctl_intvec,
+ NULL, &evms_info_level_min, &evms_info_level_max},
+ {0}
+};
+
+static struct ctl_table evms_dir_table[] = {
+ {DEV_EVMS, "evms", NULL, 0, 0555, evms_table},
+ {0}
+};
+
+static struct ctl_table dev_dir_table[] = {
+ {CTL_DEV, "dev", NULL, 0, 0555, evms_dir_table},
+ {0}
+};
+
+/**
+ * find_next_volume - locates first or next logical volume
+ * @lv: current logical volume
+ *
+ * returns the next logical volume or NULL
+ **/
+struct evms_logical_volume *
+find_next_volume(struct evms_logical_volume *lv)
+{
+ struct list_head *list_member =
+ (lv) ? &lv->volumes : &evms_logical_volumes;
+ for (lv = list_entry(list_member->next, typeof(*lv), volumes),
+ prefetch(lv->volumes.next);
+ &lv->volumes != &evms_logical_volumes;
+ lv = list_entry(lv->volumes.next, typeof(*lv), volumes),
+ prefetch(lv->volumes.next)) {
+ return lv;
+ }
+ return NULL;
+}
+
+/**
+ * find_next_volume_safe - locates first or next logical volume (safe for removes)
+ * @next_lv: ptr to next logical volume
+ *
+ * returns the next logical volume or NULL
+ **/
+struct evms_logical_volume *
+find_next_volume_safe(struct evms_logical_volume **next_lv)
+{
+ struct evms_logical_volume *lv =
+ (*next_lv) ? *next_lv :
+ list_entry(evms_logical_volumes.next, typeof(*lv), volumes);
+ *next_lv = list_entry(lv->volumes.next, typeof(*lv), volumes);
+ if (&lv->volumes != &evms_logical_volumes) {
+ return lv;
+ }
+ return NULL;
+}
+
+/**
+ * lookup_volume - finds a logical volume by minor number
+ * @minor: minor number of logical volume to be found
+ *
+ * returns the logical volume of the specified minor or NULL.
+ **/
+struct evms_logical_volume *
+lookup_volume(int minor)
+{
+ struct evms_logical_volume *lv;
+ list_for_each_entry(lv, &evms_logical_volumes, volumes) {
+ if (lv->minor == minor) {
+ return lv;
+ }
+ }
+ return NULL;
+}
+
+/**
+ * remove_logical_volume - removes a logical volume from our list
+ * @lv: logical volume to be removed.
+ *
+ * removes an existing logical volume from our list.
+ **/
+static void
+remove_logical_volume(struct evms_logical_volume *lv)
+{
+ BUG_ON(list_empty(&lv->volumes));
+ list_del_init(&lv->volumes);
+}
+
+/**********************************************************/
+/* START -- Proc FS Support functions */
+/**********************************************************/
+
+/**
+ * evms_info_read_proc - /proc/evms/info support function
+ * @page: procfs required field
+ * @start: procfs required field
+ * @off: procfs required field
+ * @count: procfs required field
+ * @eof: procfs required field
+ * @data: procfs required field
+ *
+ * cat /proc/evms/info function that provides info about EVMS.
+ **/
+static int
+evms_info_read_proc(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int sz = 0;
+ char *info_level_text = NULL;
+
+ PROCPRINT("Enterprise Volume Management System: Info\n");
+ switch (evms_info_level) {
+ case EVMS_INFO_CRITICAL:
+ info_level_text = "critical";
+ break;
+ case EVMS_INFO_SERIOUS:
+ info_level_text = "serious";
+ break;
+ case EVMS_INFO_ERROR:
+ info_level_text = "error";
+ break;
+ case EVMS_INFO_WARNING:
+ info_level_text = "warning";
+ break;
+ case EVMS_INFO_DEFAULT:
+ info_level_text = "default";
+ break;
+ case EVMS_INFO_DETAILS:
+ info_level_text = "details";
+ break;
+ case EVMS_INFO_DEBUG:
+ info_level_text = "debug";
+ break;
+ case EVMS_INFO_EXTRA:
+ info_level_text = "extra";
+ break;
+ case EVMS_INFO_ENTRY_EXIT:
+ info_level_text = "entry exit";
+ break;
+ case EVMS_INFO_EVERYTHING:
+ info_level_text = "everything";
+ break;
+ default:
+ info_level_text = "unknown";
+ break;
+ }
+ PROCPRINT("EVMS info level: %d (%s).\n",
+ evms_info_level, info_level_text);
+
+ PROCPRINT("EVMS kernel version: %d.%d.%d\n",
+ EVMS_MAJOR_VERSION,
+ EVMS_MINOR_VERSION, EVMS_PATCHLEVEL_VERSION);
+
+ PROCPRINT("EVMS IOCTL interface version: %d.%d.%d\n",
+ EVMS_IOCTL_INTERFACE_MAJOR,
+ EVMS_IOCTL_INTERFACE_MINOR, EVMS_IOCTL_INTERFACE_PATCHLEVEL);
+
+ PROCPRINT("EVMS Common Services version: %d.%d.%d\n",
+ EVMS_COMMON_SERVICES_MAJOR,
+ EVMS_COMMON_SERVICES_MINOR, EVMS_COMMON_SERVICES_PATCHLEVEL);
+
+ *eof = 1;
+
+out:
+ *start = page + off;
+ sz -= off;
+ if (sz < 0)
+ sz = 0;
+ return sz > count ? count : sz;
+}
+
+/**
+ * evms_plugins_read_proc - /proc/evms/plugins support function
+ * @page: procfs required field
+ * @start: procfs required field
+ * @off: procfs required field
+ * @count: procfs required field
+ * @eof: procfs required field
+ * @data: procfs required field
+ *
+ * cat /proc/evms/plugins that lists the currently loaded plugin modules in EVMS.
+ **/
+static int
+evms_plugins_read_proc(char *page,
+ char **start, off_t off, int count, int *eof, void *data)
+{
+ int sz = 0;
+ struct evms_plugin_header *plugin;
+
+ PROCPRINT("Enterprise Volume Management System: Plugins\n");
+ /* 0 1 1 2 2 3 3 4 4 5 5 6 6 7 */
+ /* 1 5 0 5 0 5 0 5 0 5 0 5 0 5 0 */
+ PROCPRINT(" ---------Plugin----------\n");
+ PROCPRINT(" ----id---- version\n\n");
+ spin_lock(&plugin_lock);
+ list_for_each_entry(plugin, &plugin_head, headers) {
+ PROCPRINT(" %x.%x.%x\t %d.%d.%d\n",
+ GetPluginOEM(plugin->id),
+ GetPluginType(plugin->id),
+ GetPluginID(plugin->id),
+ plugin->version.major,
+ plugin->version.minor,
+ plugin->version.patchlevel);
+ }
+ spin_unlock(&plugin_lock);
+
+out:
+ *start = page + off;
+ sz -= off;
+ if (sz < 0)
+ sz = 0;
+ return sz > count ? count : sz;
+}
+
+/**
+ * evms_volumes_read_proc - /proc/evms/volumes support function
+ * @page: procfs required field
+ * @start: procfs required field
+ * @off: procfs required field
+ * @count: procfs required field
+ * @eof: procfs required field
+ * @data: procfs required field
+ *
+ * cat /proc/evms/volumes that lists the currently exported volumes in EVMS.
+ **/
+static int
+evms_volumes_read_proc(char *page,
+ char **start, off_t off, int count, int *eof, void *data)
+{
+ struct evms_logical_volume *lv = NULL;
+ int sz = 0;
+
+ PROCPRINT("Enterprise Volume Management System: Volumes\n");
+ PROCPRINT("major minor #blocks type flags name\n\n");
+ while ((lv = find_next_volume(lv))) {
+ if (!lv->node) {
+ continue;
+ }
+ PROCPRINT("%5d %7d %16Ld %s %s %s %s%s\n",
+ EVMS_MAJOR, lv->minor,
+ (long long)lv->node->total_vsectors >> 1,
+ (lv->flags & EVMS_VOLUME_FLAG) ?
+ "evms " : "compat",
+ (lv->flags & EVMS_VOLUME_READ_ONLY) ?
+ "ro" : "rw",
+ (lv->flags & EVMS_VOLUME_PARTIAL) ?
+ "p " : " ",
+ EVMS_DIR_NAME "/", lv->name);
+ }
+out:
+ *start = page + off;
+ sz -= off;
+ if (sz < 0)
+ sz = 0;
+ return sz > count ? count : sz;
+}
+
+/**********************************************************/
+/* END -- Proc FS Support functions */
+/**********************************************************/
+
+/**********************************************************/
+/* START -- FOPS functions definitions */
+/**********************************************************/
+
+/**
+ * is_busy - determines if a block_devices is currently in use
+ * @dev: device to check
+ *
+ * determines if a block_device is in use or not
+ *
+ * returns: 0 = device is not in use
+ * -EBUSY if device is in use
+ * -ENOMEM if unable to get a bdev
+ **/
+int
+is_busy(kdev_t dev)
+{
+ struct block_device *bdev;
+
+ bdev = bdget(kdev_t_to_nr(dev));
+ if (!bdev)
+ return -ENOMEM;
+ if (bd_claim(bdev, is_busy))
+ return -EBUSY;
+ bd_release(bdev);
+ return 0;
+}
+
+/************************************************/
+/* START -- IOCTL commands -- EVMS specific */
+/************************************************/
+
+/**
+ * evms_ioctl_cmd_get_ioctl_version
+ * @arg: evms version packet
+ *
+ * retrieves the evms ioctl interface version
+ *
+ * returns: 0 = success
+ * otherwise error code
+ **/
+static int
+evms_ioctl_cmd_get_ioctl_version(void *arg)
+{
+ struct evms_version ver;
+
+ ver.major = EVMS_IOCTL_INTERFACE_MAJOR;
+ ver.minor = EVMS_IOCTL_INTERFACE_MINOR;
+ ver.patchlevel = EVMS_IOCTL_INTERFACE_PATCHLEVEL;
+
+ /* copy info to userspace */
+ if (copy_to_user(arg, &ver, sizeof (ver)))
+ return -EFAULT;
+
+ return 0;
+}
+
+/**
+ * evms_ioctl_cmd_get_version
+ * @arg: evms version packet
+ *
+ * retrieves the evms kernel version
+ *
+ * returns: 0 = success
+ * otherwise error code
+ **/
+static int
+evms_ioctl_cmd_get_version(void *arg)
+{
+ struct evms_version ver;
+
+ ver.major = EVMS_MAJOR_VERSION;
+ ver.minor = EVMS_MINOR_VERSION;
+ ver.patchlevel = EVMS_PATCHLEVEL_VERSION;
+
+ /* copy info to userspace */
+ if (copy_to_user(arg, &ver, sizeof (ver)))
+ return -EFAULT;
+
+ return 0;
+}
+
+/**
+ * evms_ioctl_cmd_get_info_level
+ * @arg: int value
+ *
+ * retrieves the evms info (syslog logging) level
+ *
+ * returns: 0 = success
+ * otherwise error code
+ **/
+static int
+evms_ioctl_cmd_get_info_level(void *arg)
+{
+ /* copy info to userspace */
+ if (copy_to_user(arg, &evms_info_level, sizeof (evms_info_level)))
+ return -EFAULT;
+
+ return 0;
+}
+
+/**
+ * evms_ioctl_cmd_set_info_level
+ * @arg: int value
+ *
+ * sets the evms info (syslog logging) level
+ *
+ * returns: 0 = success
+ * otherwise error code
+ **/
+static int
+evms_ioctl_cmd_set_info_level(void *arg)
+{
+ int temp;
+
+ /* copy info from userspace */
+ if (copy_from_user(&temp, arg, sizeof (temp)))
+ return -EFAULT;
+ evms_info_level = temp;
+
+ return 0;
+}
+
+/**
+ * evms_quiesce_volume
+ * @volume: volume to be quiesced
+ * @command: 0 = unquiesce, 1 = quiesce
+ * @minor: minor of volume to be quiesce
+ * @lock_vfs: 0 = no lock, 1 = lock
+ *
+ * this function performs the actual quiesce operation on
+ * a volume in kernel memory.
+ *
+ * when quiescing, all new I/Os to a volume are stopped,
+ * causing the calling thread to block. this thread then
+ * waits until all I/Os in progress are completed, before
+ * return control to the caller.
+ *
+ * when unquiescing, all new I/Os are allowed to proceed
+ * unencumbered, and all threads waiting (blocked) on this
+ * volume, are woken up and allowed to proceed.
+ *
+ * returns: 0 = success
+ * otherwise error code
+ */
+int
+evms_quiesce_volume(struct evms_logical_volume *volume,
+ int command, int minor, int lock_vfs)
+{
+ int rc;
+
+ LOG_DEBUG("%squiescing %s.\n",
+ ((command) ? "" : "un"), volume->name);
+
+ if (lock_vfs) {
+ /* VFS function call to sync and lock the filesystem */
+ fsync_dev_lockfs(mk_kdev(EVMS_MAJOR, minor));
+ volume->vfs_quiesced = 1;
+ }
+
+ volume->quiesced = command;
+
+ /* Command specified was "quiesce". */
+ if (command) {
+ /* After setting the volume to a quiesced state, there could
+ * still be I/O requests in progress. Wait for the request
+ * count to go to zero before continuing.
+ */
+ wait_event(volume->quiesce_wait_queue,
+ (atomic_read(&volume->requests_in_progress) == 0));
+ }
+
+ /* Send this command down the stack so lower */
+ /* layers can know about this */
+ rc = QUIESCE(volume->node, command);
+
+ /* Command specified was "unquiesce". */
+ if (!command) {
+ /* "wakeup" any I/O requests waiting on this volume */
+ wake_up(&volume->request_wait_queue);
+ if (volume->vfs_quiesced) {
+ /* VFS function call to unlock the filesystem */
+ unlockfs(mk_kdev(EVMS_MAJOR, minor));
+ volume->vfs_quiesced = 0;
+ }
+ }
+ if (rc) {
+ LOG_ERROR("error(%d) %squiescing %s.\n",
+ rc, ((command) ? "" : "un"), volume->name);
+ }
+ return rc;
+}
+
+/**
+ * evms_delete_volume
+ * @volume: logical volume being deleted
+ * @command: 0 = "soft", 1 = "hard" delete request
+ * @minor: minor of volume to be deleted
+ * @associative_minor: minor of volume associated to the @minor volume
+ *
+ * this function performs the actual delete operation on
+ * a volume to purge it from kernel memory. all structures
+ * and memory consumed by this volume will be free as well
+ * as clearing or unregistering any system services.
+ *
+ * returns: 0 = on success
+ * -EBUSY if volume is mounted
+ * otherwise error code
+ **/
+static int
+evms_delete_volume(struct evms_logical_volume *lv,
+ int command, int minor, int associative_minor)
+{
+ int rc = 0;
+ struct block_device *bdev = NULL;
+
+ /* if this is a "permament" delete */
+ /* check to make sure volume is not in use */
+ if (command) {
+ bdev = bdget(kdev_t_to_nr(mk_kdev(EVMS_MAJOR, lv->minor)));
+ if (!bdev) {
+ if (command == 1) {
+ return -ENOMEM;
+ }
+ command = 0;
+ }
+ }
+ if (command) {
+ if (bd_claim(bdev, evms_delete_volume)) {
+ if (command == 1) {
+ return -EBUSY;
+ }
+ command = 0;
+ }
+ }
+ if (command) {
+ /* invalidate the device since it is not coming back
+ * this is required incase we are re-using the minor number
+ */
+ invalidate_device(mk_kdev(EVMS_MAJOR, minor), 1);
+ }
+ /* invoke the delete ioctl at the top of the feature stack */
+ LOG_DETAILS("deleting '%s'.\n", lv->name);
+ rc = DELETE(lv->node);
+ if (rc) {
+ LOG_ERROR("error(%d) %s deleting %s.\n",
+ rc, ((command) ? "hard" : "soft"), lv->name);
+ return rc;
+ }
+ /* the volume has been deleted, do any clean up work
+ * required.
+ */
+ devfs_unregister(lv->gd->de);
+ set_device_ro(mk_kdev(EVMS_MAJOR, minor), 0);
+ del_gendisk(lv->gd);
+ put_disk(lv->gd);
+ lv->gd = NULL;
+ lv->node = NULL;
+ evms_volumes--;
+ if (command) {
+ /* if "permanent" delete, free the name
+ * and NULL the name field.
+ */
+ blk_cleanup_queue(&lv->request_queue);
+ remove_logical_volume(lv);
+ kfree(lv->name);
+ bd_release(bdev);
+ kfree(lv);
+ } else {
+ /* if "soft" delete, leave the name so
+ * we can use it to reassign the same
+ * minor to this volume after a
+ * rediscovery.
+ */
+ lv->flags = EVMS_VOLUME_SOFT_DELETED;
+ }
+ return 0;
+}
+
+/**
+ * evms_user_delete_volume
+ * @lvt: logical volume
+ * @inode: vfs ioctl parameter
+ * @file: vfs ioctl parameter
+ * @dv: delete volume ioctl packet
+ *
+ * this function, depending on the parameters, performs
+ * a "soft" or a "hard" delete. for a "soft" delete, a
+ * quiesce & delete request is queued up, to be executed
+ * at the beginning of the next rediscovery. for a
+ * "hard" delete, the target volume is quiesced and then
+ * deleted. if there is any errors attempting to delete
+ * the target, then the target is unquiesced. if an
+ * associative volume is specified it is quiesced before
+ * the target volume is quiesced, and is unquiesced
+ * after the attempt to delete the target volume.
+ **/
+static int
+evms_user_delete_volume(struct evms_logical_volume *lvt,
+ struct inode *inode, struct file *file,
+ struct evms_delete_vol_pkt *dv)
+{
+ int rc, qa = 0;
+ struct evms_logical_volume *lva = NULL;
+
+ if (!dv->command) {
+ /* "soft delete" requested */
+ lvt->flags |= (EVMS_REQUESTED_QUIESCE | EVMS_REQUESTED_DELETE);
+ if (dv->do_vfs) {
+ lvt->flags |= EVMS_REQUESTED_VFS_QUIESCE;
+ }
+ return 0;
+ }
+ /* "hard delete" requested */
+ if (dv->associative_minor) {
+ /* associative volume specified
+ *
+ * quiesce it
+ */
+ lva = lookup_volume(dv->associative_minor);
+ /* quiesce associative volume */
+ rc = evms_quiesce_volume(lva, 1,
+ dv->associative_minor,
+ 0);
+ if (!rc) {
+ qa = 1;
+ } else {
+ goto error;
+ }
+ }
+ /* quiesce target volume */
+ rc = evms_quiesce_volume(lvt, 1, dv->minor, 0);
+ if (rc) {
+ goto error;
+ }
+ /* delete the target volume */
+ rc = evms_delete_volume(lvt, dv->command, dv->minor,
+ dv->associative_minor);
+ if (rc) {
+ /* got an error undeleting...
+ *
+ * unquiesce the target
+ */
+ rc = evms_quiesce_volume(lvt, 0, dv->minor, 0);
+ }
+ error:
+ if (dv->associative_minor) {
+ /* associative volume specified
+ *
+ * unquiesce it
+ */
+ if (qa) {
+ /* only unquiesce associative
+ * if we successfully quiesced
+ * it previously.
+ */
+ rc = evms_quiesce_volume(lva, 0,
+ dv->associative_minor,
+ 0);
+ }
+ }
+ return rc;
+}
+
+/**
+ * evms_ioctl_cmd_delete_volume
+ * @inode: vfs ioctl parameter
+ * @file: vfs ioctl parameter
+ * @arg: delete volume ioctl packet
+ *
+ * this function will copy user data to/from the kernel, and
+ * validates user parameters. after validation, control
+ * is passed to worker routine evms_user_delete_volume.
+ *
+ * returns: 0 on success
+ * otherwise error code
+ **/
+static int
+evms_ioctl_cmd_delete_volume(struct inode *inode, struct file *file, ulong arg)
+{
+ int rc = 0;
+ struct evms_delete_vol_pkt tmp, *user_parms;
+ struct evms_logical_volume *lv = NULL;
+
+ user_parms = (struct evms_delete_vol_pkt *) arg;
+ /* copy user's parameters to kernel space */
+ if (copy_from_user(&tmp, user_parms, sizeof (tmp))) {
+ rc = -EFAULT;
+ goto exit;
+ }
+ /* check to make sure associative minor is in use */
+ if (tmp.associative_minor) {
+ lv = lookup_volume(tmp.associative_minor);
+ if (lv == NULL || lv->node == NULL) {
+ rc = -ENXIO;
+ goto exit;
+ }
+ }
+ /* check to make sure target minor is in use */
+ lv = lookup_volume(tmp.minor);
+ if (lv == NULL || lv->node == NULL) {
+ rc = -ENXIO;
+ } else {
+ rc = evms_user_delete_volume(lv, inode, file, &tmp);
+ }
+ exit:
+ /* copy the status value back to the user */
+ tmp.status = rc;
+ if (copy_to_user(user_parms, &tmp, sizeof (tmp))) {
+ rc = -EFAULT;
+ }
+ return rc;
+}
+
+/**
+ * evms_full_rediscover_prep
+ * @inode: vfs ioctl parameter
+ * @file: vfs ioctl parameter
+ *
+ * this function helps to prevent problems when evms is
+ * configured with the base built in statically and some
+ * plugins built as modules.
+ *
+ * in these cases, when the initial discovery is done,
+ * only the statically built modules are available for
+ * volume construction. as a result, some volumes that
+ * require the plugins built as modules (which haven't
+ * been loaded) to be fully reconstructed, may come up
+ * as compatibility volumes or partial volumes.
+ *
+ * when parts of evms are built as modules, the
+ * evms_rediscovery utility is used, to perform a secondary
+ * rediscover, after all the plugins built as modules
+ * have been loaded, to construct all the volumes
+ * requiring these plugins.
+ *
+ * however since some of the volumes, requiring the plugins
+ * built as modules, may have been already exported as
+ * compatibility or partial volumes, we need to purge these
+ * volumes from kernel's memory, so they can be rediscovered
+ * and claimed by the appropriate plugins, and reconstructed
+ * into the correct volumes.
+ *
+ * this function purges all compatibility volumes that are
+ * not in use (unmounted) and all partial volumes, prior to
+ * doing the secondary rediscover, thus allowing volumes to
+ * be rediscovered correctly.
+ *
+ * NOTE: again, this is only required in cases when a
+ * combination of plugins are built statically and as
+ * modules.
+ **/
+static void
+evms_full_rediscover_prep(struct inode *inode, struct file *file)
+{
+ struct evms_logical_volume *lv, *next_lv = NULL;
+
+ LOG_DETAILS("%s: started.\n", __FUNCTION__);
+ /* check for acceptable volumes to be deleted */
+ while ((lv = find_next_volume_safe(&next_lv))) {
+ struct evms_delete_vol_pkt dv;
+ int volume_mounted, doit;
+ kdev_t devp;
+
+ if (!lv->node)
+ continue;
+ devp = mk_kdev(EVMS_MAJOR, lv->minor);
+ volume_mounted = (is_busy(devp)) ? 1 : 0;
+ /* only proceed on volumes that are:
+ * partial volumes
+ * OR
+ * unmounted compatibility volumes
+ */
+ doit = 0;
+ if (lv->flags & EVMS_VOLUME_PARTIAL) {
+ /* do all partial volumes
+ */
+ doit = 1;
+ } else if (!(lv->flags & EVMS_VOLUME_FLAG)) {
+ /* check all compatibility volumes
+ */
+ if (!volume_mounted) {
+ /* only do unmounted volumes
+ */
+ doit = 1;
+ }
+ }
+ if (doit == 0) {
+ continue;
+ }
+ /* delete the volume from memory.
+ * do a 'soft' delete if volume
+ * is mounted, and 'hard' delete
+ * if it is not.
+ *
+ * NOTE: the delete operation will
+ * clear the bits in the flags field.
+ */
+ dv.command = (volume_mounted) ? EVMS_SOFT_DELETE :
+ EVMS_HARD_DELETE;
+ dv.minor = lv->minor;
+ dv.do_vfs = dv.associative_minor = 0;
+ evms_user_delete_volume(lv, inode, file, &dv);
+ }
+ LOG_DETAILS("%s: completed.\n", __FUNCTION__);
+}
+
+/**
+ * evms_ioctl_cmd_rediscover_volumes
+ * @inode: vfs ioctl parameter
+ * @file: vfs ioctl parameter
+ * @cmd: vfs ioctl parameter, the ioctl command
+ * @arg: vfs ioctl parameter, the rediscover ioctl packet
+ *
+ * performs a rediscovery (probing) off all specified devices and exports any
+ * newly found volumes.
+ **/
+static int
+evms_ioctl_cmd_rediscover_volumes(struct inode *inode,
+ struct file *file,
+ unsigned int cmd, ulong arg)
+{
+ int rc;
+ struct evms_rediscover_pkt tmp, *user_parms;
+ u64 *array_ptr = NULL;
+ ulong array_size = 0;
+ struct evms_logical_volume *lv = NULL, *next_lv;
+
+ /* grab the rediscover semaphore */
+ down(&red_sem);
+
+ rc = tmp.drive_count = 0;
+ user_parms = (struct evms_rediscover_pkt *) arg;
+ /* copy user's parameters to kernel space */
+ if (copy_from_user(&tmp, user_parms, sizeof (tmp))) {
+ rc = -EFAULT;
+ goto exit;
+ }
+
+ if (tmp.drive_count == REDISCOVER_ALL_DEVICES) {
+ evms_full_rediscover_prep(inode, file);
+ }
+ /* quiesce all queued volumes */
+ while ((lv = find_next_volume(lv))) {
+ if (!lv->node) {
+ continue;
+ }
+ if (!(lv->flags & EVMS_REQUESTED_QUIESCE)) {
+ continue;
+ }
+ rc = evms_quiesce_volume(lv, 1, lv->minor,
+ (lv->flags & EVMS_REQUESTED_VFS_QUIESCE) ?
+ 1 : 0);
+ }
+ /* "soft" delete all queued volumes */
+ next_lv = NULL;
+ while ((lv = find_next_volume_safe(&next_lv))) {
+ if (!lv->node) {
+ continue;
+ }
+ if (!(lv->flags & EVMS_REQUESTED_DELETE)) {
+ continue;
+ }
+ rc = evms_delete_volume(lv, EVMS_SOFT_DELETE,
+ lv->minor, 0);
+ }
+
+ if (tmp.drive_count && (tmp.drive_count != REDISCOVER_ALL_DEVICES)) {
+ /* create space for userspace drive array */
+ array_size = sizeof (*tmp.drive_array) * tmp.drive_count;
+ array_ptr = tmp.drive_array;
+ tmp.drive_array = kmalloc(array_size, GFP_KERNEL);
+ if (!tmp.drive_array) {
+ rc = -ENOMEM;
+ goto exit;
+ }
+ /* copy rediscover drive array to kernel space */
+ if (copy_from_user(tmp.drive_array, array_ptr, array_size)) {
+ rc = -EFAULT;
+ goto exit;
+ }
+ }
+ /* perform the rediscovery operation */
+ rc = evms_discover_volumes(&tmp);
+
+ /* clean up after operation */
+ if (tmp.drive_count && (tmp.drive_count != REDISCOVER_ALL_DEVICES))
+ kfree(tmp.drive_array);
+ exit:
+ /* set return code and copy info to userspace */
+ tmp.status = rc;
+ if (copy_to_user(&user_parms->status, &tmp.status, sizeof (tmp.status))) {
+ rc = -EFAULT;
+ }
+
+ /* release the rediscover semaphore */
+ up(&red_sem);
+ return rc;
+}
+
+/**
+ * evms_ioctl_cmd_get_logical_disk
+ * @arg: vfs ioctl parameter, the disk handle ioctl packet
+ *
+ * retrieves the 1st or next device (disk) handle to the caller
+ *
+ * returns: 0 on success
+ * otherwise error code
+ **/
+static struct list_head *user_disk_ptr;
+static int
+evms_ioctl_cmd_get_logical_disk(void *arg)
+{
+ struct evms_user_disk_pkt tmp, *user_parms;
+
+ user_parms = (struct evms_user_disk_pkt *) arg;
+ /* copy user's parameters to kernel space */
+ if (copy_from_user
+ (&tmp.command, &user_parms->command, sizeof (tmp.command))) {
+ return -EFAULT;
+ }
+ if (tmp.command == EVMS_FIRST_DISK)
+ user_disk_ptr = evms_device_list.next;
+ else /* tmp.command == EVMS_NEXT_DISK */
+ user_disk_ptr = user_disk_ptr->next;
+
+ if (user_disk_ptr == &evms_device_list)
+ tmp.status = EVMS_DISK_INVALID;
+ else {
+ struct evms_logical_node *node =
+ list_entry(user_disk_ptr, struct evms_logical_node, device);
+ tmp.status = EVMS_DISK_VALID;
+ tmp.disk_handle = NODE_TO_DEV_HANDLE(node);
+ }
+ /* copy info to userspace */
+ if (copy_to_user(user_parms, &tmp, sizeof (tmp))) {
+ return -EFAULT;
+ }
+ return 0;
+}
+
+/**
+ * evms_ioctl_cmd_get_logical_disk_info
+ * @arg: vfs ioctl parameter, the disk info ioctl packet
+ *
+ * fills in info about the device specified by the disk handle
+ *
+ * returns: 0 on success
+ * otherwise error code
+ **/
+static int
+evms_ioctl_cmd_get_logical_disk_info(void *arg)
+{
+ int rc;
+ struct evms_user_disk_info_pkt tmp, *user_parms;
+ struct evms_logical_node *disk_node, *mem_node;
+
+ user_parms = (struct evms_user_disk_info_pkt *) arg;
+ /* copy user's parameters to kernel space */
+ if (copy_from_user
+ (&tmp.disk_handle, &user_parms->disk_handle,
+ sizeof (tmp.disk_handle))) {
+ return -EFAULT;
+ }
+ /* check handle for validity */
+ rc = -EINVAL;
+ disk_node = DEV_HANDLE_TO_NODE(tmp.disk_handle);
+ list_for_each_entry(mem_node, &evms_device_list, device) {
+ if (mem_node == disk_node) {
+ rc = 0;
+ break;
+ }
+ }
+ if (rc) {
+ goto exit;
+ }
+ /* populate kernel copy of user's structure with appropriate info */
+ tmp.flags = disk_node->flags;
+ strcpy(tmp.disk_name, EVMS_DIR_NAME "/");
+ strcat(tmp.disk_name, disk_node->name);
+ rc = evms_cs_kernel_ioctl(disk_node, EVMS_UPDATE_DEVICE_INFO,
+ (ulong) NULL);
+ if (rc) {
+ goto exit;
+ }
+ tmp.total_sectors = disk_node->total_vsectors;
+ tmp.hardsect_size = disk_node->hardsector_size;
+ tmp.block_size = disk_node->block_size;
+ rc = GET_GEO(disk_node, &tmp.geo_cylinders, &tmp.geo_heads,
+ &tmp.geo_sectors, NULL);
+ exit:
+ /* set return code and copy info to userspace */
+ tmp.status = rc;
+ if (copy_to_user(user_parms, &tmp, sizeof (tmp))) {
+ rc = -EFAULT;
+ }
+ return rc;
+}
+
+/**
+ * evms_ioctl_cmd_sector_io - performs sector based IO to a disk device
+ * @arg: vfs ioctl parameter, the sector io ioctl packet
+ *
+ * performs 512 byte sector based io on a specified device. internally this
+ * routine will do I/O upto 64KB at a time, breaking larger requests up as
+ * needed.
+ *
+ * returns: 0 on success
+ * otherwise error code
+ **/
+#define MAX_IO_SIZE 128
+static int
+evms_ioctl_cmd_sector_io(void *arg)
+{
+ int rc;
+ struct evms_sector_io_pkt tmp, *user_parms;
+ struct evms_logical_node *disk_node, *mem_node;
+ u8 *io_buffer = NULL, *user_buffer_ptr;
+ u64 io_sector_offset, io_remaining, io_bytes, io_size = MAX_IO_SIZE;
+
+ user_parms = (struct evms_sector_io_pkt *) arg;
+ /* copy user's parameters to kernel space */
+ if (copy_from_user(&tmp, user_parms, sizeof (tmp))) {
+ return -EFAULT;
+ }
+ /* check handle for validity */
+ rc = -EINVAL;
+ disk_node = DEV_HANDLE_TO_NODE(tmp.disk_handle);
+ list_for_each_entry(mem_node, &evms_device_list, device) {
+ if (mem_node == disk_node) {
+ rc = 0;
+ break;
+ }
+ }
+ if (rc) {
+ goto exit;
+ }
+ /* allocate a io buffer upto 64Kbytes in size */
+ if (tmp.sector_count < MAX_IO_SIZE) {
+ io_size = tmp.sector_count;
+ }
+ /* allocate buffer large enough to hold a single sector */
+ io_buffer = kmalloc(io_size << EVMS_VSECTOR_SIZE_SHIFT, GFP_KERNEL);
+ if (!io_buffer) {
+ rc = -ENOMEM;
+ goto exit;
+ }
+ /* perform io with specified disk */
+ io_remaining = tmp.sector_count;
+ io_sector_offset = 0;
+ user_buffer_ptr = tmp.buffer_address;
+ while (io_remaining) {
+ /* compute the io_size for this pass */
+ io_size = (io_remaining >= MAX_IO_SIZE) ?
+ MAX_IO_SIZE : io_remaining;
+ io_bytes = io_size << EVMS_VSECTOR_SIZE_SHIFT;
+ /* for writes, copy a sector from user to kernel */
+ if (tmp.io_flag == EVMS_SECTOR_IO_WRITE) {
+ /* copy sector from user data buffer */
+ if (copy_from_user
+ (io_buffer, user_buffer_ptr, io_bytes)) {
+ rc = -EFAULT;
+ goto exit;
+ }
+ }
+ /* perform IO */
+ rc = INIT_IO(disk_node, tmp.io_flag,
+ io_sector_offset + tmp.starting_sector,
+ io_size, io_buffer);
+ if (rc) {
+ goto exit;
+ }
+ if (tmp.io_flag != EVMS_SECTOR_IO_WRITE) {
+ /* copy sector to user data buffer */
+ if (copy_to_user(user_buffer_ptr, io_buffer, io_bytes)) {
+ rc = -EFAULT;
+ goto exit;
+ }
+ }
+ user_buffer_ptr += io_bytes;
+ tmp.buffer_address += io_bytes;
+ io_sector_offset += io_size;
+ io_remaining -= io_size;
+ }
+exit:
+ /* if the sector_buffer was allocated, free it */
+ if (io_buffer)
+ kfree(io_buffer);
+
+ /* copy the status value back to the user */
+ tmp.status = rc;
+ if (copy_to_user(user_parms, &tmp, sizeof (tmp)))
+ rc = -EFAULT;
+
+ return rc;
+}
+
+#undef MAX_IO_SIZE
+
+/**
+ * evms_ioctl_cmd_get_minor - retrieves the volume minor device number
+ * @arg: vfs ioctl parameter, the get minor ioctl packet
+ *
+ * retrieves the minor device number for the 1st or the next volume exported by EVMS.
+ *
+ * NOTE: this routine will look for, and purge any volumes previous mounted as
+ * corrupt that are no longer mounted.
+ *
+ * returns: 0 on success
+ * otherwise error code
+ **/
+static int user_minor;
+static int
+evms_ioctl_cmd_get_minor(void *arg)
+{
+ struct evms_user_minor_pkt tmp, *user_parms;
+ struct evms_logical_volume *lv;
+
+ user_parms = (struct evms_user_minor_pkt *) arg;
+ /* copy user's parameters to kernel space */
+ if (copy_from_user
+ (&tmp.command, &user_parms->command, sizeof (tmp.command))) {
+ return -EFAULT;
+ }
+ if (tmp.command == EVMS_FIRST_VOLUME) {
+ user_minor = 0;
+ lv = NULL;
+ } else { /* tmp.command == EVMS_NEXT_VOLUME */
+ lv = lookup_volume(user_minor);
+ }
+
+ tmp.status = EVMS_VOLUME_INVALID;
+ while ((lv = find_next_volume(lv))) {
+ user_minor = lv->minor;
+ /* see if any corrupt volumes have been
+ * unmounted. If so, clean up the entry
+ * in evms_logical_volumes, and
+ * don't report the volume to the user.
+ */
+ if (lv->flags & EVMS_VOLUME_CORRUPT) {
+ if (!is_busy(mk_kdev(EVMS_MAJOR, user_minor))) {
+ /* clear logical volume structure
+ * for this volume so it may be
+ * reused.
+ */
+ LOG_WARNING("ioctl_get_minor: found unmounted "
+ "%s volume(%u,%u,%s).\n",
+ ((lv->flags & EVMS_VOLUME_SOFT_DELETED) ?
+ "'soft deleted'" : ""),
+ EVMS_MAJOR, user_minor, lv->name);
+ LOG_WARNING(" releasing minor(%d) used by "
+ "volume(%s)!\n",
+ user_minor, lv->name);
+ blk_cleanup_queue(&lv->request_queue);
+ kfree(lv->name);
+ list_del_init(&lv->volumes);
+ kfree(lv);
+ }
+ }
+ if (lv->node || (lv->flags & EVMS_VOLUME_CORRUPT)) {
+ tmp.status = EVMS_VOLUME_VALID;
+ tmp.minor = user_minor;
+ break;
+ }
+ }
+ /* copy info to userspace */
+ if (copy_to_user(user_parms, &tmp, sizeof (tmp)))
+ return -EFAULT;
+
+ return 0;
+}
+
+/**
+ * evms_ioctl_cmd_get_volume_data - retrieves info about a volume
+ * @arg: vfs ioctl parameter, the get volume data ioctl packet
+ *
+ * retrieves the info about a specified volume
+ *
+ * returns: 0 on success
+ * otherwise error code
+ **/
+static int
+evms_ioctl_cmd_get_volume_data(void *arg)
+{
+ int rc = 0;
+ struct evms_volume_data_pkt tmp, *user_parms;
+ struct evms_logical_volume *lv = NULL;
+
+ user_parms = (struct evms_volume_data_pkt *) arg;
+ /* copy user's parameters to kernel space */
+ if (copy_from_user(&tmp, user_parms, sizeof (tmp))) {
+ return -EFAULT;
+ }
+ lv = lookup_volume(tmp.minor);
+ if (lv == NULL || lv->node == NULL) {
+ rc = -ENODEV;
+ goto exit;
+ }
+ tmp.flags = lv->flags;
+ strcpy(tmp.volume_name, EVMS_DIR_NAME "/");
+ strcat(tmp.volume_name, lv->name);
+ exit:
+ /* copy return code and info to userspace */
+ tmp.status = rc;
+ if (copy_to_user(user_parms, &tmp, sizeof (tmp))) {
+ rc = -EFAULT;
+ }
+ return rc;
+}
+
+/**
+ * evms_ioctl_cmd_get_plugin - retrieves info about the 1st or next loaded kernel plugin
+ * @arg: vfs ioctl parameter, the registered plugin ioctl packet
+ *
+ * retrieves the info about the 1st or next loaded kernel plugin
+ *
+ * returns: 0 on success
+ * otherwise error code
+ **/
+static int plugin_id;
+static int
+evms_ioctl_cmd_get_plugin(void *arg)
+{
+ struct evms_kernel_plugin_pkt tmp, *user_parms;
+ struct evms_plugin_header *plugin;
+
+ user_parms = (struct evms_kernel_plugin_pkt *) arg;
+ /* copy user's parameters to kernel space */
+ if (copy_from_user
+ (&tmp.command, &user_parms->command, sizeof (tmp.command))) {
+ return -EFAULT;
+ }
+ spin_lock(&plugin_lock);
+ plugin = list_entry(&plugin_head, typeof(*plugin), headers);
+ /* if the command is not 0, then verify
+ * that ioctl_reg_record is pointing to
+ * current and valid plugin header.
+ */
+ if (tmp.command) { /* tmp.command == EVMS_NEXT_PLUGIN */
+ list_for_each_entry(plugin, &plugin_head, headers) {
+ if (plugin_id == plugin->id) {
+ break;
+ }
+ }
+ if (&plugin->headers == &plugin_head) {
+ tmp.command = EVMS_FIRST_PLUGIN;
+ }
+ }
+ if (tmp.command == EVMS_FIRST_PLUGIN) {
+ plugin_id = 0;
+ }
+ if (!list_empty(&plugin_head)) {
+ plugin = list_entry(plugin->headers.next, typeof(*plugin), headers);
+ if (&plugin->headers != &plugin_head) {
+ plugin_id = plugin->id;
+ }
+ }
+ /* populate the user's buffer */
+ tmp.status = EVMS_PLUGIN_INVALID;
+ tmp.id = 0;
+ if (plugin_id) {
+ tmp.id = plugin->id;
+ tmp.version = plugin->version;
+ tmp.status = EVMS_PLUGIN_VALID;
+ }
+ spin_unlock(&plugin_lock);
+ /* copy info to userspace */
+ if (copy_to_user(user_parms, &tmp, sizeof (tmp))) {
+ return -EFAULT;
+ }
+ return 0;
+}
+
+/**
+ * evms_ioctl_cmd_plugin_ioctl - sends a plugin specific ioctl to a specified plugin
+ * @inode: vfs ioctl parameter
+ * @file: vfs ioctl parameter
+ * @cmd: vfs ioctl parameter
+ * @arg: vfs ioctl parameter, the plugin ioctl packet
+ *
+ * routes a plugin specific ioctl to the specified plugin
+ *
+ * returns: 0 on success
+ * otherwise error code
+ **/
+static int
+evms_ioctl_cmd_plugin_ioctl(struct inode *inode,
+ struct file *file, unsigned int cmd, ulong arg)
+{
+ int rc;
+ struct evms_plugin_ioctl_pkt tmp, *user_parms;
+ struct evms_plugin_header *plugin;
+
+ user_parms = (struct evms_plugin_ioctl_pkt *) arg;
+ /* copy user's parameters to kernel space */
+ if (copy_from_user(&tmp, user_parms, sizeof (tmp))) {
+ return -EFAULT;
+ }
+ /* search for the specified plugin */
+ rc = -ENOPKG;
+ spin_lock(&plugin_lock);
+ list_for_each_entry(plugin, &plugin_head, headers) {
+ /* check for the specified feature id */
+ if (plugin->id == tmp.feature_id) {
+ /* check that entry point is used */
+ rc = -ENOSYS;
+ if (plugin->fops->direct_ioctl) {
+ spin_unlock(&plugin_lock);
+ rc = DIRECT_IOCTL(plugin, inode, file, cmd, arg);
+ spin_lock(&plugin_lock);
+ }
+ break;
+ }
+ }
+ spin_unlock(&plugin_lock);
+ /* copy the status value back to the user */
+ tmp.status = rc;
+ if (copy_to_user(user_parms, &tmp, sizeof (tmp))) {
+ rc = -EFAULT;
+ }
+ return rc;
+}
+
+/**
+ * evms_ioctl_cmd_kernel_partial_csum - invokes the kernel's csum_partial routine
+ * @arg: vfs ioctl parameter, the compute csum ioctl packet
+ *
+ * provides userspace access to the kernel's csum_partial routine
+ *
+ * returns: 0 on success
+ * otherwise error code
+ **/
+#define MAX_BUFFER_SIZE 65536
+static int
+evms_ioctl_cmd_kernel_partial_csum(void *arg)
+{
+ int rc = 0;
+ struct evms_compute_csum_pkt tmp, *user_parms;
+ u8 *user_buffer_ptr, *buffer = NULL;
+ u64 remaining_bytes, compute_size = MAX_BUFFER_SIZE;
+ unsigned int insum;
+
+ user_parms = (struct evms_compute_csum_pkt *) arg;
+ /* copy user's parameters to kernel space */
+ if (copy_from_user(&tmp, user_parms, sizeof (tmp))) {
+ return -EFAULT;
+ }
+ /* allocate a io buffer upto 64Kbytes in size */
+ if (tmp.buffer_size < MAX_BUFFER_SIZE) {
+ compute_size = tmp.buffer_size;
+ }
+ /* allocate buffer large enough to hold a single sector */
+ buffer = kmalloc(compute_size, GFP_KERNEL);
+ if (!buffer) {
+ rc = -ENOMEM;
+ goto exit;
+ }
+ /* perform io with specified disk */
+ insum = tmp.insum;
+ remaining_bytes = tmp.buffer_size;
+ user_buffer_ptr = tmp.buffer_address;
+ while (remaining_bytes) {
+ /* compute the compute_size for this pass */
+ compute_size = (remaining_bytes >= MAX_BUFFER_SIZE) ?
+ MAX_BUFFER_SIZE : remaining_bytes;
+ /* copy into kernel from user data buffer */
+ if (copy_from_user(buffer, user_buffer_ptr, compute_size)) {
+ rc = -EFAULT;
+ goto exit;
+ }
+ /* compute the checksum for this pass */
+ tmp.outsum = csum_partial(buffer, tmp.buffer_size, insum);
+ /* set up for another possible pass */
+ insum = tmp.outsum;
+ /* update loop progress variables */
+ user_buffer_ptr += compute_size;
+ tmp.buffer_address += compute_size;
+ remaining_bytes -= compute_size;
+ }
+ exit:
+ /* if the sector_buffer was allocated, free it */
+ if (buffer)
+ kfree(buffer);
+
+ /* copy the status value back to the user */
+ tmp.status = rc;
+ if (copy_to_user(user_parms, &tmp, sizeof (tmp))) {
+ rc = -EFAULT;
+ }
+ return rc;
+}
+
+#undef MAX_BUFFER_SIZE
+
+/**
+ * evms_ioctl_cmd_get_bmap - computes the physical dev/sector pair from a logical dev/sector
+ * @inode: vfs ioctl parameter
+ * @file: vfs ioctl parameter
+ * @cmd: vfs ioctl parameter
+ * @arg: vfs ioctl parameter, the get bmap ioctl packet
+ *
+ * takes a logical volume's device/sector pair and returns the physical device/sector. this
+ * is typically used by loaders such as lilo, to read system files using BIOS during boot.
+ *
+ * returns: 0 on success
+ * otherwise error code
+ **/
+static int
+evms_ioctl_cmd_get_bmap(struct inode *inode,
+ struct file *file, unsigned int cmd, ulong arg)
+{
+ int rc;
+ struct evms_get_bmap_pkt tmp, *user_parms;
+ struct evms_logical_volume *lv;
+
+ user_parms = (struct evms_get_bmap_pkt *) arg;
+ /* copy user's parameters to kernel space */
+ if (copy_from_user(&tmp, user_parms, sizeof (tmp))) {
+ return -EFAULT;
+ }
+ /* pass the ioctl down the volume stack */
+ lv = lookup_volume(minor(inode->i_rdev));
+ rc = IOCTL(lv->node, inode, file, cmd, (ulong) & tmp);
+ /* copy the status value back to the user */
+ tmp.status = rc;
+ if (copy_to_user(user_parms, &tmp, sizeof (tmp))) {
+ rc = -EFAULT;
+ }
+ return rc;
+}
+
+/**
+ * evms_ioctl_cmd_process_notify_event - allows a process to register for a signal on an event
+ * @arg: vfs ioctl parameter, the notify ioctl packet
+ *
+ * lets a process register for a signal notification when a specified kernel event occurs.
+ *
+ * returns: 0 on success
+ * otherwise error code
+ **/
+static int
+evms_ioctl_cmd_process_notify_event(ulong arg)
+{
+ int rc = 0, found = 0;
+ struct evms_notify_pkt tmp, *user_parms;
+ struct evms_event *event = NULL;
+ struct evms_kevent *kevent = NULL;
+
+ user_parms = (struct evms_notify_pkt *) arg;
+ /* copy user's parameters to kernel space */
+ if (copy_from_user(&tmp, user_parms, sizeof (tmp))) {
+ return -EFAULT;
+ }
+ /* check to see if PID has already been registered
+ * for this event.
+ */
+ list_for_each_entry(kevent, &evms_notify_list, list) {
+ event = &kevent->uevent;
+ if ((event->pid == tmp.eventry.pid) &&
+ (event->eventid == tmp.eventry.eventid)) {
+ found = 1;
+ break;
+ }
+ }
+ if (tmp.command) { /* tmp.command == EVMS_REGISTER_EVENT */
+ /* registration code */
+ if (found) {
+ rc = -EBUSY;
+ LOG_ERROR("error(%d): pid(%d) already registered to "
+ "receive signal(%d) on event(%d).\n", rc,
+ tmp.eventry.pid, tmp.eventry.signo,
+ tmp.eventry.eventid);
+ } else {
+ /* register this pid/event type */
+ kevent =
+ kmalloc(sizeof (struct evms_kevent), GFP_KERNEL);
+ if (!kevent) {
+ rc = -ENOMEM;
+ LOG_ERROR("error(%d) allocating event "
+ "structure.\n", rc);
+ } else {
+ kevent->uevent.pid = tmp.eventry.pid;
+ kevent->uevent.eventid = tmp.eventry.eventid;
+ kevent->uevent.signo = tmp.eventry.signo;
+ list_add(&kevent->list, &evms_notify_list);
+ }
+ }
+ } else { /* tmp.command == EVMS_UNREGISTER_EVENT */
+ /* unregistration code */
+ if (!found) {
+ rc = -ENODATA;
+ LOG_ERROR("error(%d) attempting to unregister a "
+ "non-registered pid(%d) on event(%d).\n",
+ rc, tmp.eventry.pid, tmp.eventry.eventid);
+ } else {
+ list_del_init(&kevent->list);
+ kfree(event);
+ }
+ }
+ /* copy the status value back to the user */
+ tmp.status = rc;
+ if (copy_to_user(user_parms, &tmp, sizeof (tmp))) {
+ rc = -EFAULT;
+ }
+ return rc;
+}
+
+/**
+ * evms_ioctl_cmd_check_mount_status - determines if the specified volume is current mounted
+ * @inode: vfs ioctl parameter
+ * @file: vfs ioctl parameter
+ * @arg: vfs ioctl parameter, the mount status ioctl packet
+ *
+ * allows userspace to query the kernel to know if the specified volume is mounted (in use)
+ *
+ * returns: 0 on success
+ * otherwise error code
+ **/
+static int
+evms_ioctl_cmd_check_mount_status(struct inode *inode, struct file *file,
+ ulong arg)
+{
+ struct evms_mount_status_pkt tmp, *user_parms;
+
+ user_parms = (struct evms_mount_status_pkt *) arg;
+ /* copy user's parameters to kernel space */
+ if (copy_from_user(&tmp, user_parms, sizeof (tmp))) {
+ return -EFAULT;
+ }
+ tmp.mounted = (is_busy(mk_kdev(EVMS_MAJOR, tmp.minor))) ? 1 : 0;
+ /* copy the status value back to the user */
+ tmp.status = 0;
+ if (copy_to_user(user_parms, &tmp, sizeof (tmp))) {
+ return -EFAULT;
+ }
+ return 0;
+}
+
+/************************************************/
+/* END -- IOCTL commands -- EVMS specific */
+/************************************************/
+
+/************************************************/
+/* START -- IOCTL commands -- Volume specific */
+/************************************************/
+
+/************************************************/
+/* END -- IOCTL commands -- Volume specific */
+/************************************************/
+
+/************************************************/
+/* START -- IOCTL main */
+/************************************************/
+
+/**
+ * evms_ioctl - the main ioctl routing function
+ * @inode: vfs ioctl parameter, the inode
+ * @file: vfs ioctl parameter, the file
+ * @cmd: vfs ioctl parameter, the command
+ * @arg: vfs ioctl parameter, the argument
+ *
+ * EVMS' main ioctl entry point and routing function
+ *
+ * returns: 0 on success
+ * otherwise error code
+ **/
+static int
+evms_ioctl(struct inode *inode, struct file *file, unsigned int cmd, ulong arg)
+{
+ ulong minor = 0;
+ int rc = 0;
+
+ /* check user access */
+ if (!capable(CAP_SYS_ADMIN))
+ return -EACCES;
+
+ if (!inode)
+ return -EINVAL;
+
+ /* get the minor */
+ minor = minor(inode->i_rdev);
+ LOG_EXTRA("ioctl: minor(%lu), dir(%d), size(%d), type(%d), nr(%d)\n",
+ minor, (cmd >> _IOC_DIRSHIFT) & _IOC_DIRMASK,
+ (cmd >> _IOC_SIZESHIFT) & _IOC_SIZEMASK,
+ (cmd >> _IOC_TYPESHIFT) & _IOC_TYPEMASK,
+ (cmd >> _IOC_NRSHIFT) & _IOC_NRMASK);
+
+ /* process the IOCTL commands */
+ if (!minor) {
+ /* process all EVMS specific commands */
+ switch (cmd) {
+ case EVMS_GET_IOCTL_VERSION:
+ rc = evms_ioctl_cmd_get_ioctl_version((void *)
+ arg);
+ break;
+ case EVMS_GET_VERSION:
+ rc = evms_ioctl_cmd_get_version((void *) arg);
+ break;
+ case EVMS_GET_INFO_LEVEL:
+ rc = evms_ioctl_cmd_get_info_level((void *)
+ arg);
+ break;
+ case EVMS_SET_INFO_LEVEL:
+ rc = evms_ioctl_cmd_set_info_level((void *)
+ arg);
+ break;
+ case EVMS_REDISCOVER_VOLUMES:
+ rc = evms_ioctl_cmd_rediscover_volumes(inode,
+ file, cmd, arg);
+ break;
+ case EVMS_GET_LOGICAL_DISK:
+ rc = evms_ioctl_cmd_get_logical_disk((void *)
+ arg);
+ break;
+ case EVMS_GET_LOGICAL_DISK_INFO:
+ rc = evms_ioctl_cmd_get_logical_disk_info((void *)
+ arg);
+ break;
+ case EVMS_SECTOR_IO:
+ rc = evms_ioctl_cmd_sector_io((void *) arg);
+ break;
+ case EVMS_GET_MINOR:
+ rc = evms_ioctl_cmd_get_minor((void *) arg);
+ break;
+ case EVMS_GET_VOLUME_DATA:
+ rc = evms_ioctl_cmd_get_volume_data((void *)
+ arg);
+ break;
+ case EVMS_DELETE_VOLUME:
+ rc = evms_ioctl_cmd_delete_volume(inode, file, arg);
+ break;
+ case EVMS_GET_PLUGIN:
+ rc = evms_ioctl_cmd_get_plugin((void *) arg);
+ break;
+ case EVMS_PLUGIN_IOCTL:
+ rc = evms_ioctl_cmd_plugin_ioctl(inode, file, cmd, arg);
+ break;
+ case EVMS_COMPUTE_CSUM:
+ rc = evms_ioctl_cmd_kernel_partial_csum((void *)
+ arg);
+ break;
+ case EVMS_PROCESS_NOTIFY_EVENT:
+ rc = evms_ioctl_cmd_process_notify_event(arg);
+ break;
+ case EVMS_CHECK_MOUNT_STATUS:
+ rc = evms_ioctl_cmd_check_mount_status(inode, file,
+ arg);
+ break;
+ default:
+ rc = -ENOTTY;
+ break;
+ }
+ } else {
+ struct evms_logical_volume *lv;
+ struct evms_logical_node *node = NULL;
+
+ /* insure this minor points to a valid volume */
+ lv = lookup_volume(minor);
+ if (lv == NULL || lv->node == NULL) {
+ return -ENXIO;
+ }
+ node = lv->node;
+
+ /* process Volume specific commands */
+ switch (cmd) {
+ case EVMS_GET_IOCTL_VERSION:
+ rc = evms_ioctl_cmd_get_ioctl_version((void *)
+ arg);
+ break;
+ case EVMS_GET_BMAP:
+ rc = evms_ioctl_cmd_get_bmap(inode, file, cmd, arg);
+ break;
+ default:
+ rc = IOCTL(node, inode, file, cmd, arg);
+ break;
+ }
+ }
+ return rc;
+}
+
+/************************************************/
+/* END -- IOCTL main */
+/************************************************/
+
+/************************************************/
+/* START -- CHECK MEDIA CHANGE */
+/************************************************/
+
+/**
+ * evms_check_media_change
+ * @dev: the device to check
+ *
+ * checks to see if the media change flag is set for this device
+ *
+ * returns: 1 = media change detected
+ * 0 = no media change detected
+ * otherwise error code
+ **/
+static int
+evms_check_media_change(kdev_t dev)
+{
+ int rc = 0;
+ struct evms_logical_volume *lv;
+
+ lv = lookup_volume(minor(dev));
+ if (lv == NULL || lv->node == NULL) {
+ return -ENXIO;
+ }
+ /* Wait here if the volume is quiesced. */
+ atomic_inc(&lv->requests_in_progress);
+ do {
+ if (!lv->quiesced)
+ break;
+ if (atomic_dec_and_test(&lv->requests_in_progress))
+ wake_up(&lv->quiesce_wait_queue);
+ wait_event(lv->request_wait_queue, (!lv->quiesced));
+ atomic_inc(&lv->requests_in_progress);
+ } while (0);
+ /* Volume node may have gone away while we were waiting. */
+ if (!lv->node) {
+ return -ENXIO;
+ }
+ if (lv->flags & EVMS_DEVICE_REMOVABLE) {
+ rc = CHECK_MEDIA_CHANGE(lv->node, dev);
+ }
+ if (atomic_dec_and_test(&lv->requests_in_progress) &&
+ lv->quiesced) {
+ wake_up(&lv->quiesce_wait_queue);
+ }
+ if (rc < 0) {
+ LOG_ERROR("error(%d) checking media change on '%s'.\n",
+ rc, lv->name);
+ }
+ return rc;
+}
+
+/************************************************/
+/* END -- CHECK MEDIA CHANGE */
+/************************************************/
+
+/**
+ * evms_check_for_device_changes - looks for any new/removed devices/media
+ * @inode: vfs ioctl parameter, the inode
+ * @file: vfs ioctl parameter, the file
+ *
+ * checks for new or removed devices as well as changed removable media. to accomplish
+ * this, this routine will completely purge from kernel memory entire volumes that may
+ * only partially reside on the change devices. once purged, all new devices and devices
+ * that purged volumes resided on, are re-probed and the resulting volumes are made
+ * available.
+ *
+ * NOTE: to maximize the effectiveness of this function, it is called on every OPEN
+ * of the EVMS block_device. Everytime the user tools are invoked, or a volume is
+ * mounted, this check will happen and the user will the see the updated results
+ * without any manual steps.
+ *
+ * returns: 0 on success
+ * otherwise error code
+ **/
+static int
+evms_check_for_device_changes(struct inode *inode, struct file *file)
+{
+ int rc = 0, something_changed = 0, i;
+ struct evms_rediscover_pkt kernel_rd_pckt = { 0, 0, NULL };
+ struct list_head changed_list, new_device_list;
+ struct evms_logical_node *disk, *next;
+ struct evms_logical_volume *lv = NULL, *next_lv;
+
+ /* grab the rediscover semaphore */
+ down(&red_sem);
+
+ INIT_LIST_HEAD(&changed_list);
+ INIT_LIST_HEAD(&new_device_list);
+
+ /* check for new devices
+ *
+ * put all new devices on the disk list so they
+ * will be included in the rediscovery process.
+ */
+ evms_discover_logical_disks(&new_device_list);
+ if (!list_empty(&new_device_list)) {
+ LOG_DETAILS("%s: new devices detected.\n", __FUNCTION__);
+ something_changed++;
+ /* put these new nodes on the disk list */
+ list_for_each_entry_safe(disk, next, &new_device_list, discover) {
+ list_del_init(&disk->discover);
+ list_add(&disk->removable, &changed_list);
+ }
+ }
+
+ /* check all devices for changed removable media
+ *
+ * scan the evms device list and issue check
+ * media change on each removable media device.
+ * put all removable devices that indicate a
+ * media change on the changed list.
+ *
+ * also scan for devices that have been unplugged
+ * or contain corrupt volumes.
+ */
+ list_for_each_entry_safe(disk, next, &evms_device_list, device) {
+ int add_to_list = 0;
+ /* only really check removable media devices */
+ if (disk->flags & EVMS_DEVICE_REMOVABLE) {
+ /* check for media change */
+ rc = CHECK_MEDIA_CHANGE(disk, mk_kdev(0,0));
+ if (rc < 0) {
+ LOG_ERROR("error(%d) doing CHECK_MEDIA_CHANGE "
+ "on '%s'.\n", rc, disk->name);
+ } else if (rc == 1) {
+ add_to_list = 1;
+ }
+ }
+ /* check for devices that were present
+ * earlier, but are gone now (from being
+ * unplugged or unloaded driver).
+ */
+ if (disk->flags & EVMS_DEVICE_UNAVAILABLE) {
+ add_to_list = 1;
+ }
+ if (add_to_list) {
+ something_changed++;
+ list_add(&disk->removable, &changed_list);
+ }
+ }
+ /* log a statement that we detected changed media.
+ */
+ if (!list_empty(&changed_list)) {
+ LOG_DETAILS("%s: media change detected.\n", __FUNCTION__);
+ }
+
+ /* check for volumes with removed removable media.
+ * mark the volumes that reside on changed media.
+ */
+ lv = NULL;
+ while ((lv = find_next_volume(lv))) {
+ if (!lv->node)
+ continue;
+ if (!(lv->flags & EVMS_DEVICE_REMOVABLE))
+ continue;
+ if (evms_check_media_change(mk_kdev(EVMS_MAJOR, lv->minor)) <= 0)
+ continue;
+ /* remember which volumes have changed media */
+ lv->flags |= EVMS_MEDIA_CHANGED;
+ something_changed++;
+ }
+
+ /* check for removed devices */
+ lv = NULL;
+ while ((lv = find_next_volume(lv))) {
+ int status;
+ if (!lv->node)
+ continue;
+ /* check for device status */
+ status = 0;
+ rc = DEVICE_STATUS(lv->node, &status);
+ if (rc) {
+ LOG_ERROR("error(%d) doing DEVICE_STATUS "
+ "on '%s'.\n", rc, lv->name);
+ continue;
+ }
+ if (!(status & EVMS_DEVICE_UNAVAILABLE)) {
+ continue;
+ }
+ /* remember which volumes have changed media */
+ lv->flags |= EVMS_DEVICE_UNPLUGGED;
+ something_changed++;
+ }
+
+ /* do we have some work to do? */
+ if (something_changed) {
+ /* check for volumes to be deleted */
+ lv = NULL;
+ while ((lv = find_next_volume(lv))) {
+ if (!lv->node)
+ continue;
+ /* only proceed on volumes with:
+ * changed media,
+ * hot-unplugged devices,
+ * & partial volumes
+ */
+ if (!(lv->flags &
+ (EVMS_MEDIA_CHANGED |
+ EVMS_VOLUME_PARTIAL | EVMS_DEVICE_UNPLUGGED)))
+ continue;
+ /* gather the disk's needing to be
+ * rediscovered to rebuild this
+ * volume.
+ *
+ * this will locate other disks that
+ * the volume resides on that don't
+ * indicate media change.
+ */
+ rc = DEVICE_LIST(lv->node, &changed_list);
+ if (rc) {
+ LOG_ERROR("%s: error(%d) retrieving underlying "
+ "disk list for '%s', skipping ...\n",
+ __FUNCTION__, rc, lv->name);
+ continue;
+ }
+ /* quiesce all the changed volumes
+ * prior to being deleted.
+ */
+ rc = evms_quiesce_volume(lv, 1, lv->minor, 0);
+ if (rc) {
+ LOG_ERROR("%s: error(%d) attempting to quiesce "
+ "'%s%s'.\n", __FUNCTION__, rc,
+ EVMS_DIR_NAME "/", lv->name);
+ }
+ }
+
+ /* we need to revalidate all the changed
+ * media. this is accomplished by issuing
+ * the revalidate disk ioctl to each device
+ * with changed media. the device manager
+ * remembers which devices indicated
+ * media changed (set by check media
+ * changed ioctl issued earlier), and will
+ * only issue the revalidate disk ioctl to
+ * those disks one time.
+ *
+ * NOTE:
+ * this needs to be done BEFORE deleting
+ * the volumes because deleting the
+ * last segment on disk will cause the
+ * associated disk node to freed, and we
+ * will not be able to issue the
+ * revalidate disk ioctl after that.
+ */
+ list_for_each_entry(disk, &changed_list, removable) {
+ /* only really do removable media devices */
+ if (disk->flags & EVMS_MEDIA_CHANGED) {
+ /* go revalidate the change media */
+ rc = REVALIDATE(disk, mk_kdev(0,0));
+ if (rc) {
+ LOG_ERROR("%s: error(%d) attempting to "
+ "revalidate '%s%s'.\n",
+ __FUNCTION__, rc,
+ EVMS_DIR_NAME "/", lv->name);
+ }
+ }
+ }
+
+ /* delete all the affected volumes */
+ next_lv = NULL;
+ while ((lv = find_next_volume_safe(&next_lv))) {
+ if (!lv->node)
+ continue;
+ /* only proceed on volumes with:
+ * changed media,
+ * hot-unplugged devices,
+ * & partial volumes
+ */
+ if (!(lv->flags &
+ (EVMS_MEDIA_CHANGED |
+ EVMS_VOLUME_PARTIAL | EVMS_DEVICE_UNPLUGGED)))
+ continue;
+ /* only delete quiesced volumes */
+ if (!lv->quiesced)
+ continue;
+ /* delete the volume from memory.
+ * do a 'soft' delete if volume
+ * is mounted, and 'hard' delete
+ * if it is not.
+ *
+ * NOTE: the delete operation will
+ * clear the bits in the flags field.
+ */
+ rc = evms_delete_volume(lv, 2, lv->minor, 0);
+ }
+
+ /* at this point all devices indicating
+ * media change that had volumes on them
+ * should be gone. however, we could still
+ * have devices indicating media change
+ * that had no volumes on them in the disk
+ * list. we need to delete these devices
+ * from kernel memory and the global device
+ * list.
+ */
+ list_for_each_entry_safe(disk, next, &evms_device_list, device) {
+ if (disk->flags & EVMS_MEDIA_CHANGED) {
+ DELETE(disk);
+ }
+ }
+
+ /* all the devices that indicated media
+ * change should be gone, both from kernel
+ * memory and evms device list. we now
+ * need to remove any references to these
+ * devices from the disk list.
+ *
+ * when removable media is installed, it
+ * will get detected in the device manager's
+ * rediscovery as a new device and added to
+ * the discover list.
+ */
+ list_for_each_entry_safe(disk, next, &changed_list, removable) {
+ int still_in_changed_list;
+ struct evms_logical_node *edl_node;
+
+ still_in_changed_list = 0;
+ list_for_each_entry(edl_node, &evms_device_list, device) {
+ if (edl_node == disk) {
+ still_in_changed_list = 1;
+ break;
+ }
+ }
+ if (still_in_changed_list == 0) {
+ list_del_init(&disk->removable);
+ }
+ }
+
+ /* build the in-kernel rediscover packet */
+
+ /* allocate the space for the drive_array in
+ * the struct evms_rediscover packet. to do this
+ * we need to count the number of disk nodes,
+ * then allocate the necessary space.
+ */
+ /* count the disk nodes */
+ list_for_each_entry(disk, &changed_list, removable) {
+ kernel_rd_pckt.drive_count++;
+ }
+ /* allocate the space */
+ if (kernel_rd_pckt.drive_count) {
+ kernel_rd_pckt.drive_array
+ = kmalloc(kernel_rd_pckt.drive_count *
+ sizeof (u64), GFP_KERNEL);
+ if (!kernel_rd_pckt.drive_array) {
+ rc = -ENOMEM;
+ LOG_ERROR("%s: error(%d) allocating rediscover "
+ "drive array.\n", __FUNCTION__, rc);
+ return rc;
+ }
+ }
+ /* populate the drive array
+ *
+ * this also frees the changed_list which is useful
+ * if we had an error allocating the drive array.
+ */
+ i = 0;
+ list_for_each_entry_safe(disk, next, &changed_list, removable) {
+ /* remove this disk from the disk list */
+ list_del_init(&disk->removable);
+ /* add this disk to rediscover packet
+ */
+ kernel_rd_pckt.drive_array[i++] =
+ NODE_TO_DEV_HANDLE(disk);
+ }
+ /* perform the rediscovery operation */
+ rc = evms_discover_volumes(&kernel_rd_pckt);
+ if (kernel_rd_pckt.drive_count) {
+ kfree(kernel_rd_pckt.drive_array);
+ }
+ LOG_DETAILS("%s: rediscover completed.\n", __FUNCTION__);
+ }
+
+ /* release the rediscover semaphore */
+ up(&red_sem);
+
+ return rc;
+}
+
+/************************************************/
+/* START -- REVALIDATE DISK */
+/************************************************/
+
+/**
+ * evms_revalidate_disk - routes revalidate ioctls to all underlying volume devices
+ * @dev: the volume whose underlying devices will be revalidated
+ *
+ * ripples the revalidate request down the EVMS volume feature stack and routes to
+ * all underlying dasd devices. This is typically done after issuing a change
+ * media change which reports a media change, so the kernel can update its disk
+ * info for the new media.
+ *
+ * returns: 0 on success
+ * otherwise error code
+ **/
+static int
+evms_revalidate_disk(kdev_t dev)
+{
+ int rc;
+ struct evms_logical_volume *lv;
+
+ lv = lookup_volume(minor(dev));
+ if (lv == NULL || lv->node == NULL) {
+ return -ENXIO;
+ }
+ /* Wait here if the volume is quiesced. */
+ atomic_inc(&lv->requests_in_progress);
+ do {
+ if (!lv->quiesced)
+ break;
+ if (atomic_dec_and_test(&lv->requests_in_progress))
+ wake_up(&lv->quiesce_wait_queue);
+ wait_event(lv->request_wait_queue, (!lv->quiesced));
+ atomic_inc(&lv->requests_in_progress);
+ } while (0);
+ /* Volume node may have gone away while we were waiting. */
+ if (!lv->node) {
+ return -ENXIO;
+ }
+ rc = REVALIDATE(lv->node, dev);
+ if (atomic_dec_and_test(&lv->requests_in_progress) &&
+ lv->quiesced) {
+ wake_up(&lv->quiesce_wait_queue);
+ }
+ return rc;
+}
+
+/************************************************/
+/* END -- REVALIDATE DISK */
+/************************************************/
+
+/************************************************/
+/* START -- OPEN */
+/************************************************/
+
+/**
+ * evms_open - open a volume or EVMS block device
+ * @inode: vfs ioctl parameter, the inode
+ * @file: vfs ioctl parameter, the file
+ *
+ * opens a volume or the EVMS block device. this will also check for device changes
+ * prior to rippling the OPEN request down the volume feature stack to the underlying
+ * devices.
+ *
+ * returns: 0 on success
+ * otherwise error code
+ **/
+static int
+evms_open(struct inode *inode, struct file *file)
+{
+ int minor, rc;
+ struct evms_logical_volume *lv;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EACCES;
+ if (!inode)
+ return -EINVAL;
+ rc = evms_check_for_device_changes(inode, file);
+ if (rc) {
+ return rc;
+ }
+ minor = minor(inode->i_rdev);
+ if (minor) {
+ lv = lookup_volume(minor);
+ if (lv == NULL || lv->node == NULL) {
+ return -ENXIO;
+ }
+ /* Wait here if the volume is quiesced. */
+ atomic_inc(&lv->requests_in_progress);
+ do {
+ if (!lv->quiesced)
+ break;
+ if (atomic_dec_and_test(&lv->requests_in_progress))
+ wake_up(&lv->quiesce_wait_queue);
+ wait_event(lv->request_wait_queue, (!lv->quiesced));
+ atomic_inc(&lv->requests_in_progress);
+ } while (0);
+ /* Volume node may have gone away while we were waiting. */
+ if (!lv->node) {
+ return -ENXIO;
+ }
+ rc = OPEN(lv->node, inode, file);
+ if (atomic_dec_and_test(&lv->requests_in_progress) &&
+ lv->quiesced) {
+ wake_up(&lv->quiesce_wait_queue);
+ }
+ if (rc) {
+ LOG_ERROR("error(%d) opening volume '%s'.\n",
+ rc, lv->name);
+ return rc;
+ }
+ }
+ return 0;
+}
+
+/************************************************/
+/* END -- OPEN */
+/************************************************/
+
+/************************************************/
+/* START -- RELEASE */
+/************************************************/
+
+/**
+ * evms_release - release or close a volume or EVMS block device
+ * @inode: vfs ioctl parameter, the inode
+ * @file: vfs ioctl parameter, the file
+ *
+ * releases a volume or the EVMS block device, rippling the RELEASE request
+ * down the volume feature stack to the underlying devices.
+ *
+ * returns: 0 on success
+ * otherwise error code
+ **/
+static int
+evms_release(struct inode *inode, struct file *file)
+{
+ int minor;
+ struct evms_logical_volume *lv = NULL;
+
+ if (!inode)
+ return -EINVAL;
+ minor = minor(inode->i_rdev);
+ if (minor) {
+ int rc;
+ lv = lookup_volume(minor);
+ if (lv == NULL || lv->node == NULL) {
+ return -ENXIO;
+ }
+ /* Wait here if the volume is quiesced. */
+ atomic_inc(&lv->requests_in_progress);
+ do {
+ if (!lv->quiesced)
+ break;
+ if (atomic_dec_and_test(&lv->requests_in_progress))
+ wake_up(&lv->quiesce_wait_queue);
+ wait_event(lv->request_wait_queue, (!lv->quiesced));
+ atomic_inc(&lv->requests_in_progress);
+ } while (0);
+ /* Volume node may have gone away while we were waiting. */
+ if (!lv->node) {
+ return -ENXIO;
+ }
+ rc = CLOSE(lv->node, inode, file);
+ if (atomic_dec_and_test(&lv->requests_in_progress) &&
+ lv->quiesced) {
+ wake_up(&lv->quiesce_wait_queue);
+ }
+ if (rc) {
+ LOG_ERROR("error(%d) releasing volume '%s'.\n",
+ rc, lv->name);
+ return rc;
+ }
+ }
+ return 0;
+}
+
+/************************************************/
+/* END -- RELEASE */
+/************************************************/
+
+/**
+ * evms_fops - EVMS block device operations table
+ **/
+struct block_device_operations evms_fops = {
+ owner:THIS_MODULE,
+ open:evms_open,
+ release:evms_release,
+ ioctl:evms_ioctl,
+ check_media_change:evms_check_media_change,
+ revalidate:evms_revalidate_disk
+};
+
+/**********************************************************/
+/* END -- FOPS functions definitions */
+/**********************************************************/
+
+/**********************************************************/
+/* START -- RUNTIME support functions */
+/**********************************************************/
+
+/**
+ * evms_find_queue - finds the appropriate per volume request queue
+ * @dev: the volume whose queue to find
+ *
+ * locates the request queue for the specified volume
+ *
+ * returns: NULL if request queue not found
+ * volume's request queue
+ **/
+request_queue_t *
+evms_find_queue(kdev_t dev)
+{
+ struct evms_logical_volume *lv = lookup_volume(minor(dev));
+ if (lv && lv->node)
+ return &lv->request_queue;
+ return NULL;
+}
+EXPORT_SYMBOL(evms_find_queue);
+
+/**
+ * evms_make_request_fn - IO request submission routine for EVMS
+ * @q: request queue
+ * @bio: submitted bio
+ *
+ * routes the bio to the appropriate volume's submit_io entry point
+ *
+ * returns: 0 always
+ **/
+int
+evms_make_request_fn(request_queue_t * q, struct bio *bio)
+{
+ struct evms_logical_volume *lv =
+ lookup_volume(minor(to_kdev_t(bio->bi_bdev->bd_dev)));
+ if (!lv || !lv->node) {
+ goto error;
+ }
+
+ /* Wait here if the volume is quiesced. */
+ atomic_inc(&lv->requests_in_progress);
+ do {
+ if (!lv->quiesced)
+ break;
+ if (atomic_dec_and_test(&lv->requests_in_progress))
+ wake_up(&lv->quiesce_wait_queue);
+ wait_event(lv->request_wait_queue, (!lv->quiesced));
+ atomic_inc(&lv->requests_in_progress);
+ } while (0);
+
+ /* Volume node may have gone away while we were waiting. */
+ if (!lv->node) {
+ goto error;
+ }
+
+ SUBMIT_IO(lv->node, bio);
+
+ if (atomic_dec_and_test(&lv->requests_in_progress) &&
+ lv->quiesced) {
+ wake_up(&lv->quiesce_wait_queue);
+ }
+ return 0;
+
+error:
+ LOG_ERROR("request for unknown logical volume [minor(%d)].\n",
+ minor(to_kdev_t(bio->bi_bdev->bd_dev)));
+ bio_io_error(bio, bio->bi_size);
+ return 0;
+}
+
+/**********************************************************/
+/* END -- RUNTIME support functions */
+/**********************************************************/
+
+/**
+ * evms_notify_reboot - EVMS' reboot notification response function
+ * @this: notifer_block
+ * @code: reboot reason code
+ * @x: unused here
+ *
+ * this function gets called at shutdown time and is used
+ * to remove any evms controlled volumes from memory, thus
+ * allowing any plugins needing to flush internal caches
+ * to do so.
+ *
+ * returns: NOTIFY_DONE
+ */
+int
+evms_notify_reboot(struct notifier_block *this, ulong code, void *x)
+{
+ struct evms_logical_volume *lv, *next_lv;
+
+ switch (code) {
+ case SYS_DOWN:
+ case SYS_HALT:
+ case SYS_POWER_OFF:
+ LOG_DEFAULT("stopping all evms controlled volumes.\n");
+
+ /* quiesce all volumes */
+ lv = NULL;
+ while ((lv = find_next_volume(lv))) {
+ if (!lv->node) {
+ continue;
+ }
+ evms_quiesce_volume(lv, 1, lv->minor, 0);
+ }
+ /* delete all volumes
+ *
+ * to ensure this work under the
+ * most circumstances, a "soft"
+ * delete will be done. this will
+ * handle the strange case of a
+ * volume still being mounted.
+ */
+ next_lv = NULL;
+ while ((lv = find_next_volume_safe(&next_lv))) {
+ if (!lv->node)
+ continue;
+ /* only delete quiesced volumes */
+ if (!lv->quiesced)
+ continue;
+ /* delete the volume from memory.
+ * do a 'soft' delete if volume
+ * is mounted, and 'hard' delete
+ * if it is not.
+ */
+ evms_delete_volume(lv, 2, lv->minor, 0);
+ }
+ }
+ return NOTIFY_DONE;
+}
+
+/**
+ * evms_notifier - reboot notification registration record
+ **/
+static struct notifier_block evms_notifier = {
+ .notifier_call = evms_notify_reboot,
+ .next = NULL,
+ .priority = INT_MAX, /* before any real devices */
+};
+
+/* Hooks to be able to force-load the Passthru plugin. */
+extern int evms_passthru_init(void);
+extern void evms_passthru_exit(void);
+
+void evms_load_passthru(void)
+{
+ evms_passthru_init();
+}
+
+void evms_unload_passthru(void)
+{
+ evms_passthru_exit();
+}
+
+/**
+ * slab_pool_alloc
+ * @gfp_mask: GFP allocation flag
+ * @data: mempool prototype required fields
+ *
+ * mempool allocate function
+ **/
+static void *
+slab_pool_alloc(int gfp_mask, void *data)
+{
+ return kmem_cache_alloc(data, gfp_mask);
+}
+
+/**
+ * slab_pool_free
+ * @ptr: mempool prototype required fields
+ * @data: mempool prototype required fields
+ *
+ * mempool free function
+ **/
+static void
+slab_pool_free(void *ptr, void *data)
+{
+ kmem_cache_free(data, ptr);
+}
+
+int evms_can_unload(void)
+{
+ return (atomic_read(&red_sem.count) < 1) ? -EBUSY : 0;
+}
+
+/**
+ * evms_init_module
+ *
+ * This function runs once at module initialization and performs the one time
+ * EVMS setup tasks of allocating memory, initializing variables, and
+ * registering the block device.
+ **/
+static int __init
+evms_init_module(void)
+{
+ int rc;
+
+ LOG_DEFAULT("EVMS v%d.%d.%d initializing .... info level(%d).\n",
+ EVMS_MAJOR_VERSION, EVMS_MINOR_VERSION,
+ EVMS_PATCHLEVEL_VERSION, evms_info_level);
+
+ INIT_LIST_HEAD(&evms_device_list);
+ INIT_LIST_HEAD(&evms_fbottom_list);
+ INIT_LIST_HEAD(&evms_notify_list);
+ INIT_LIST_HEAD(&evms_logical_volumes);
+ init_MUTEX(&red_sem);
+
+ /* Initialize the evms_io_notify pool */
+ evms_io_notify_slab = kmem_cache_create("EVMS IO Notify",
+ sizeof (struct evms_io_notify),
+ 0, SLAB_HWCACHE_ALIGN,
+ NULL, NULL);
+ if (!evms_io_notify_slab) {
+ LOG_ERROR("error(%d): unable to create EVMS IO Notify cache.",
+ -ENOMEM);
+ return -ENOMEM;
+ }
+ evms_io_notify_pool = mempool_create(256, slab_pool_alloc,
+ slab_pool_free,
+ evms_io_notify_slab);
+ if (!evms_io_notify_pool) {
+ kmem_cache_destroy(evms_io_notify_slab);
+ LOG_ERROR("error(%d): unable to create EVMS IO Notify pool.",
+ -ENOMEM);
+ return -ENOMEM;
+ }
+
+ /* Register the block device */
+ rc = register_blkdev(EVMS_MAJOR, EVMS_DIR_NAME, &evms_fops);
+ if (rc) {
+ LOG_CRITICAL("error(%d) calling register_blkdev()\n", rc);
+ return -EINVAL;
+ }
+
+ /* Register with devfs. A NULL return is not fatal,
+ * since devfs just might not be running.
+ */
+ evms_dir_devfs_handle = devfs_mk_dir(NULL, EVMS_DIR_NAME, NULL);
+ if (evms_dir_devfs_handle) {
+ evms_blk_devfs_handle =
+ devfs_register(evms_dir_devfs_handle, EVMS_DEV_NAME,
+ DEVFS_FL_DEFAULT, EVMS_MAJOR, 0,
+ S_IFBLK | S_IRUGO | S_IWUGO,
+ &evms_fops, NULL);
+ if (!evms_blk_devfs_handle) {
+ LOG_DETAILS("NULL return from devfs_register() for \"%s\"\n",
+ EVMS_DEV_NAME);
+ }
+ }
+
+ blk_dev[EVMS_MAJOR].queue = evms_find_queue;
+ blk_queue_make_request(BLK_DEFAULT_QUEUE(EVMS_MAJOR),
+ evms_make_request_fn);
+
+ /* Create the proc and sysctl entries. */
+ evms_cs_get_evms_proc_dir();
+ if (evms_proc_dir) {
+ create_proc_read_entry("info", 0, evms_proc_dir,
+ evms_info_read_proc, NULL);
+ create_proc_read_entry("plugins", 0, evms_proc_dir,
+ evms_plugins_read_proc, NULL);
+ create_proc_read_entry("volumes", 0, evms_proc_dir,
+ evms_volumes_read_proc, NULL);
+ }
+
+ evms_table_header = register_sysctl_table(dev_dir_table, 1);
+
+ register_reboot_notifier(&evms_notifier);
+
+ evms_ioctl32_register();
+
+ evms_load_passthru();
+
+ return 0;
+}
+
+/**
+ * evms_exit_module
+ *
+ * This function runs when the EVMS block device is being unloaded. it
+ * unloads the passthru plugin, unregisters the ioctl32 handlers and the
+ * reboot notifier. Then it cleans up the proc and sysctl entries and the
+ * I/O request queue. Finally it unregisters with devfs and the block
+ * layer and frees up all allocated memory.
+ **/
+static void __exit
+evms_exit_module(void)
+{
+ LOG_DEFAULT("EVMS v%d.%d.%d unloading ....\n",
+ EVMS_MAJOR_VERSION,
+ EVMS_MINOR_VERSION, EVMS_PATCHLEVEL_VERSION);
+
+ evms_unload_passthru();
+
+ evms_ioctl32_unregister();
+
+ unregister_reboot_notifier(&evms_notifier);
+
+ if (evms_proc_dir) {
+ remove_proc_entry("volumes", evms_proc_dir);
+ remove_proc_entry("plugins", evms_proc_dir);
+ remove_proc_entry("info", evms_proc_dir);
+ remove_proc_entry("evms", NULL);
+ }
+
+ unregister_sysctl_table(evms_table_header);
+
+ blk_cleanup_queue(BLK_DEFAULT_QUEUE(EVMS_MAJOR));
+ blk_dev[EVMS_MAJOR].queue = NULL;
+
+ devfs_unregister(evms_dir_devfs_handle);
+
+ unregister_blkdev(EVMS_MAJOR, EVMS_DIR_NAME);
+
+ mempool_destroy(evms_io_notify_pool);
+ kmem_cache_destroy(evms_io_notify_slab);
+}
+
+/**
+ * evms_cluster_init
+ *
+ * a placeholder for cluster enablement
+ **/
+void
+evms_cluster_init(int nodeid, int clusterid)
+{
+ /* dummy */
+ return;
+}
+
+EXPORT_SYMBOL(evms_cluster_init);
+
+/**
+ * evms_cluster_shutdown
+ *
+ * a placeholder for cluster enablement
+ **/
+int
+evms_cluster_shutdown(void)
+{
+ /* dummy */
+ return -1;
+}
+
+EXPORT_SYMBOL(evms_cluster_shutdown);
+
+/**
+ * evms_boot_info_level - parses the ascii evms info level parm on the loader's boot line
+ * @str: the ascii info level to be parsed
+ *
+ * parses the ascii evms info level parm on the loader's boot line
+ *
+ * returns: 1 (always)
+ **/
+int __init
+evms_boot_info_level(char *str)
+{
+ int evms_boot_info_level = (int) simple_strtoul(str, NULL, 10);
+ if (evms_boot_info_level) {
+ evms_info_level = evms_boot_info_level;
+ }
+ return 1;
+}
+
+__setup("evms_info_level=", evms_boot_info_level);
+module_init(evms_init_module);
+module_exit(evms_exit_module);
+MODULE_LICENSE("GPL");
+

2002-10-10 20:26:18

by Kevin Corry

[permalink] [raw]
Subject: [PATCH] EVMS core (8/9) evms_biosplit.h

Greetings,

Part 8 of the EVMS core driver.

This header file defines some of the common functions used by the
EVMS core to perform bio splitting. The remaining bio-splitting code
is in services.c. It would be our hope that this code might be taken
out of EVMS in the future and turned into a common kernel service.

The general idea behind this approach is for each plugin to determine
if a bio spans one of its internal boundaries (e.g. a request crosses
the boundary of an LVM PE or an MD chunk). If so, the plugin calls the
core biosplit code with the desired bio and boundary offset and gets
two bio's back. The first one can then be processed immediately. The
second one must then be checked again for further boundary conditions,
and possibly split again. After all necessary splits have taken place
and all portions driven to the devices, the core code handles the
callbacks and completes the original bio when all the split portions
are done.

Kevin Corry
[email protected]
http://evms.sourceforge.net/



diff -Naur linux-2.5.41/include/linux/evms_biosplit.h linux-2.5.41-evms/include/linux/evms_biosplit.h
--- linux-2.5.41/include/linux/evms_biosplit.h Wed Dec 31 18:00:00 1969
+++ linux-2.5.41-evms/include/linux/evms_biosplit.h Thu Oct 10 11:20:31 2002
@@ -0,0 +1,143 @@
+/*
+ *
+ *
+ * Copyright (c) International Business Machines Corp., 2000 - 2002
+ *
+ * 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
+ *
+ *
+ */
+/*
+ *
+ * BIO splitting header file
+ *
+ * This is temporarily placed here until a common generic solution
+ * is available.
+ *
+ */
+
+#include <linux/mempool.h>
+
+static mempool_t *my_bio_split_pool, *my_bio_pool;
+static kmem_cache_t *my_bio_split_slab, *my_bio_pool_slab;
+
+/**
+ * slab_pool_alloc
+ * @gfp_mask: GFP allocation flag
+ * @data: mempool prototype required fields
+ *
+ * mempool allocate function
+ **/
+static void *
+slab_pool_alloc(int gfp_mask, void *data)
+{
+ return kmem_cache_alloc(data, gfp_mask);
+}
+
+/**
+ * slab_pool_free
+ * @ptr: mempool prototype required fields
+ * @data: mempool prototype required fields
+ *
+ * mempool free function
+ **/
+static void
+slab_pool_free(void *ptr, void *data)
+{
+ kmem_cache_free(data, ptr);
+}
+
+/** bio_cache_ctor - initializes bio elements
+ * @foo: ptr to element
+ * @cachep: ptr to slab cache
+ * @flags: slab flags
+ *
+ * this function initializes the bi_io_vec field in the
+ * bio's allocates from our private bio pool.
+ **/
+#define EVMS_BIO_VEC_SIZE (sizeof(struct bio_vec) * (BIO_MAX_PAGES+1))
+static void bio_cache_ctor(void * foo, kmem_cache_t * cachep, unsigned long flags)
+{
+ if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) ==
+ SLAB_CTOR_CONSTRUCTOR) {
+ struct bio *bio = (struct bio *)foo;
+ memset(bio, 0, (sizeof(*bio) + EVMS_BIO_VEC_SIZE));
+ bio->bi_io_vec = (struct bio_vec *)((char *)bio + sizeof(*bio));
+ }
+}
+
+static int
+bio_split_setup(char * split_name, char * bio_name)
+{
+ /* initialize MY bio split record pool */
+ my_bio_split_slab = kmem_cache_create(split_name,
+ sizeof
+ (struct bio_split_cb),
+ 0, SLAB_HWCACHE_ALIGN,
+ NULL, NULL);
+ if (!my_bio_split_slab) {
+ LOG_ERROR("error(%d): unable to create %s cache.\n",
+ -ENOMEM, split_name);
+ return -ENOMEM;
+ }
+ my_bio_split_pool = mempool_create(256,
+ slab_pool_alloc,
+ slab_pool_free,
+ my_bio_split_slab);
+ if (!my_bio_split_pool) {
+ LOG_ERROR("error(%d): unable to create %s pool.",
+ -ENOMEM, split_name);
+ return -ENOMEM;
+ }
+
+ /* initialize MY bio pool */
+ my_bio_pool_slab = kmem_cache_create(bio_name,
+ (sizeof (struct bio) +
+ EVMS_BIO_VEC_SIZE),
+ 0, SLAB_HWCACHE_ALIGN,
+ bio_cache_ctor, NULL);
+ if (!my_bio_pool_slab) {
+ LOG_ERROR("error(%d): unable to create %s cache.\n",
+ -ENOMEM, split_name);
+ return -ENOMEM;
+ }
+ my_bio_pool = mempool_create(256,
+ slab_pool_alloc,
+ slab_pool_free,
+ my_bio_pool_slab);
+ if (!my_bio_pool) {
+ LOG_ERROR("error(%d): unable to create %s pool.",
+ -ENOMEM, split_name);
+ return -ENOMEM;
+ }
+ return 0;
+}
+
+static void
+bio_split_remove(void)
+{
+ if (my_bio_pool) {
+ mempool_destroy(my_bio_pool);
+ }
+ if (my_bio_pool_slab) {
+ kmem_cache_destroy(my_bio_pool_slab);
+ }
+ if (my_bio_split_pool) {
+ mempool_destroy(my_bio_split_pool);
+ }
+ if (my_bio_split_slab) {
+ kmem_cache_destroy(my_bio_split_slab);
+ }
+}
??9

2002-10-10 20:29:04

by Kevin Corry

[permalink] [raw]
Subject: [PATCH] EVMS core (9/9) misc

Greetings,

Part 9 of the EVMS core driver.

This is a series of patches and files required to properly configure and
build EVMS as part of the kernel. Included here is:

- patch for the MAINTAINERS file to include EVMS contact information.
- patch for arch/i386/config.in to provide a configuration menu option
for EVMS. Patches for other architectures are available, and will be
submitted in the future.
- patch for drivers/Makefile to process the EVMS directory.
- EVMS-specific configuration files and Makefiles. These currently only
contain information on building the core driver. As addition plugins are
submitted, the appropriate patches against these files will be provided.
- patch for include/linux/list.h to add a list_for_each_entry_safe() macro.
- patch for include/linux/major.h to add the EVMS major number (117).
- patch for include/linux/sysctl.h to add entries for EVMS.

Kevin Corry
[email protected]
http://evms.sourceforge.net/



diff -Naur linux-2.5.41/MAINTAINERS linux-2.5.41-evms/MAINTAINERS
--- linux-2.5.41/MAINTAINERS Mon Oct 7 13:24:40 2002
+++ linux-2.5.41-evms/MAINTAINERS Thu Oct 10 11:23:27 2002
@@ -556,6 +556,13 @@
W: http://opensource.creative.com/
S: Maintained

+ENTERPRISE VOLUME MANAGEMENT SYSTEM (EVMS)
+P: Mark Peloquin, Steve Pratt, Kevin Corry
+M: [email protected], [email protected], [email protected]
+L: [email protected]
+W: http://www.sourceforge.net/projects/evms/
+S: Supported
+
ETHEREXPRESS-16 NETWORK DRIVER
P: Philip Blundell
M: [email protected]
diff -Naur linux-2.5.41/arch/i386/config.in linux-2.5.41-evms/arch/i386/config.in
--- linux-2.5.41/arch/i386/config.in Mon Oct 7 13:24:02 2002
+++ linux-2.5.41-evms/arch/i386/config.in Thu Oct 10 11:24:05 2002
@@ -370,6 +370,8 @@
fi
endmenu

+source drivers/evms/Config.in
+
source drivers/md/Config.in

source drivers/message/fusion/Config.in
diff -Naur linux-2.5.41/drivers/Makefile linux-2.5.41-evms/drivers/Makefile
--- linux-2.5.41/drivers/Makefile Mon Oct 7 13:24:49 2002
+++ linux-2.5.41-evms/drivers/Makefile Thu Oct 10 11:22:27 2002
@@ -41,5 +41,6 @@
obj-$(CONFIG_BLUEZ) += bluetooth/
obj-$(CONFIG_HOTPLUG_PCI) += hotplug/
obj-$(CONFIG_ISDN_BOOL) += isdn/
+obj-$(CONFIG_EVMS) += evms/

include $(TOPDIR)/Rules.make
diff -Naur linux-2.5.41/drivers/evms/Config.in linux-2.5.41-evms/drivers/evms/Config.in
--- linux-2.5.41/drivers/evms/Config.in Wed Dec 31 18:00:00 1969
+++ linux-2.5.41-evms/drivers/evms/Config.in Thu Oct 10 11:18:21 2002
@@ -0,0 +1,25 @@
+#
+# EVMS kernel configuration
+#
+
+mainmenu_option next_comment
+comment 'Enterprise Volume Management System'
+
+tristate 'EVMS Kernel Runtime' CONFIG_EVMS
+
+if [ "$CONFIG_EVMS" != "n" ]; then
+ choice ' EVMS Debug Level' \
+ "Critical CONFIG_EVMS_INFO_CRITICAL \
+ Serious CONFIG_EVMS_INFO_SERIOUS \
+ Error CONFIG_EVMS_INFO_ERROR \
+ Warning CONFIG_EVMS_INFO_WARNING \
+ Default CONFIG_EVMS_INFO_DEFAULT \
+ Details CONFIG_EVMS_INFO_DETAILS \
+ Debug CONFIG_EVMS_INFO_DEBUG \
+ Extra CONFIG_EVMS_INFO_EXTRA \
+ Entry_Exit CONFIG_EVMS_INFO_ENTRY_EXIT \
+ Everything CONFIG_EVMS_INFO_EVERYTHING" Default
+fi
+
+endmenu
+
diff -Naur linux-2.5.41/drivers/evms/Makefile linux-2.5.41-evms/drivers/evms/Makefile
--- linux-2.5.41/drivers/evms/Makefile Wed Dec 31 18:00:00 1969
+++ linux-2.5.41-evms/drivers/evms/Makefile Thu Oct 10 11:18:13 2002
@@ -0,0 +1,8 @@
+#
+# Makefile for the kernel EVMS driver and modules.
+#
+
+obj-$(CONFIG_EVMS) += core/
+
+include $(TOPDIR)/Rules.make
+
diff -Naur linux-2.5.41/drivers/evms/core/Config.help linux-2.5.41-evms/drivers/evms/core/Config.help
--- linux-2.5.41/drivers/evms/core/Config.help Wed Dec 31 18:00:00 1969
+++ linux-2.5.41-evms/drivers/evms/core/Config.help Thu Oct 10 11:16:58 2002
@@ -0,0 +1,30 @@
+CONFIG_EVMS
+ EVMS runtime driver. This is a plugin-based framework for volume
+ management, and combines support for partitioning, software RAID,
+ LVM, and more into a single interface.
+
+ User-space tools are required to perform administration of EVMS logical
+ volumes. Please visit <http://www.sourceforge.net/projects/evms> for
+ more details on downloading and installing these tools.
+
+ This driver is also available as a modules called evmscore.o ( = code
+ which can be inserted and removed from the running kernel whenever you
+ want). If you want to compile it as a module, say M here and read
+ <file:Documentation/modules.txt>.
+
+CONFIG_EVMS_INFO_CRITICAL
+ Set the level for kernel messages from EVMS. Each level on the list
+ produces message for that level and all levels above it. Thus, level
+ "Critical" only logs the most critical messages (and thus the fewest),
+ whereas level "Everything" produces more information that will probably
+ ever be useful. Level "Default" is a good starting point. Level "Debug"
+ is good if you are having problems with EVMS and want more basic info
+ on what's going on during the volume discovery process.
+
+ EVMS also supports a boot-time kernel parameter to set the info level.
+ To use this method, specify "evms_info_level=5" at boot time, or add the
+ line "append = "evms_info_level=5"" to your lilo.conf file (replacing 5
+ with your desired info level). See include/linux/evms.h for the
+ numerical definitions of the info levels. To use this boot-time parameter,
+ the EVMS core driver must be statically built into the kernel (not as a
+ module).
diff -Naur linux-2.5.41/drivers/evms/core/Makefile linux-2.5.41-evms/drivers/evms/core/Makefile
--- linux-2.5.41/drivers/evms/core/Makefile Wed Dec 31 18:00:00 1969
+++ linux-2.5.41-evms/drivers/evms/core/Makefile Thu Oct 10 11:17:16 2002
@@ -0,0 +1,41 @@
+#
+# Makefile for the EVMS core.
+#
+
+export-objs := evms.o services.o
+
+evmscore-objs := evms.o services.o discover.o passthru.o
+
+obj-$(CONFIG_EVMS) += evmscore.o
+
+EXTRA_CFLAGS=-DEVMS_INFO_LEVEL=EVMS_INFO_DEFAULT
+ifeq ($(CONFIG_EVMS_INFO_CRITICAL),y)
+ EXTRA_CFLAGS=-DEVMS_INFO_LEVEL=EVMS_INFO_CRITICAL
+endif
+ifeq ($(CONFIG_EVMS_INFO_SERIOUS),y)
+ EXTRA_CFLAGS=-DEVMS_INFO_LEVEL=EVMS_INFO_SERIOUS
+endif
+ifeq ($(CONFIG_EVMS_INFO_ERROR),y)
+ EXTRA_CFLAGS=-DEVMS_INFO_LEVEL=EVMS_INFO_ERROR
+endif
+ifeq ($(CONFIG_EVMS_INFO_WARNING),y)
+ EXTRA_CFLAGS=-DEVMS_INFO_LEVEL=EVMS_INFO_WARNING
+endif
+ifeq ($(CONFIG_EVMS_INFO_DETAILS),y)
+ EXTRA_CFLAGS=-DEVMS_INFO_LEVEL=EVMS_INFO_DETAILS
+endif
+ifeq ($(CONFIG_EVMS_INFO_DEBUG),y)
+ EXTRA_CFLAGS=-DEVMS_INFO_LEVEL=EVMS_INFO_DEBUG
+endif
+ifeq ($(CONFIG_EVMS_INFO_EXTRA),y)
+ EXTRA_CFLAGS=-DEVMS_INFO_LEVEL=EVMS_INFO_EXTRA
+endif
+ifeq ($(CONFIG_EVMS_INFO_ENTRY_EXIT),y)
+ EXTRA_CFLAGS=-DEVMS_INFO_LEVEL=EVMS_INFO_ENTRY_EXIT
+endif
+ifeq ($(CONFIG_EVMS_INFO_EVERYTHING),y)
+ EXTRA_CFLAGS=-DEVMS_INFO_LEVEL=EVMS_INFO_EVERYTHING
+endif
+
+include $(TOPDIR)/Rules.make
+
diff -Naur linux-2.5.41/include/linux/list.h linux-2.5.41-evms/include/linux/list.h
--- linux-2.5.41/include/linux/list.h Mon Oct 7 13:22:55 2002
+++ linux-2.5.41-evms/include/linux/list.h Thu Oct 10 14:13:52 2002
@@ -240,6 +240,20 @@
pos = list_entry(pos->member.next, typeof(*pos), member), \
prefetch(pos->member.next))

+/**
+ * list_for_each_entry_safe - iterate over list of a given type, safe against removal of list entry
+ * @pos: the type * to use as a loop counter.
+ * @n: another type * to use as temporary storage
+ * @head: the head for your list.
+ * @member: the name of the list_struct within the struct.
+ */
+#define list_for_each_entry_safe(pos, n, head, member) \
+ for (pos = list_entry((head)->next, typeof(*pos), member), \
+ n = list_entry(pos->member.next, typeof(*pos), member); \
+ &pos->member != (head); \
+ pos = n, \
+ n = list_entry(pos->member.next, typeof(*pos), member))
+
#endif /* __KERNEL__ || _LVM_H_INCLUDE */

#endif
diff -Naur linux-2.5.41/include/linux/major.h linux-2.5.41-evms/include/linux/major.h
--- linux-2.5.41/include/linux/major.h Mon Oct 7 15:57:47 2002
+++ linux-2.5.41-evms/include/linux/major.h Wed Jul 10 09:45:46 2002
@@ -140,6 +140,8 @@

#define LVM_CHAR_MAJOR 109 /* Logical Volume Manager */

+#define EVMS_MAJOR 117 /* Enterprise Volume Management System */
+
#define RTF_MAJOR 150
#define RAW_MAJOR 162

diff -Naur linux-2.5.41/include/linux/sysctl.h linux-2.5.41-evms/include/linux/sysctl.h
--- linux-2.5.41/include/linux/sysctl.h Mon Oct 7 15:57:47 2002
+++ linux-2.5.41-evms/include/linux/sysctl.h Mon Oct 7 15:57:44 2002
@@ -578,7 +578,8 @@
DEV_HWMON=2,
DEV_PARPORT=3,
DEV_RAID=4,
- DEV_MAC_HID=5
+ DEV_MAC_HID=5,
+ DEV_EVMS=6,
};

/* /proc/sys/dev/cdrom */
@@ -594,6 +595,18 @@
/* /proc/sys/dev/parport */
enum {
DEV_PARPORT_DEFAULT=-3
+};
+
+/* /proc/sys/dev/evms */
+enum {
+ DEV_EVMS_INFO_LEVEL=1,
+ DEV_EVMS_MD=2
+};
+
+/* /proc/sys/dev/evms/raid */
+enum {
+ DEV_EVMS_MD_SPEED_LIMIT_MIN=1,
+ DEV_EVMS_MD_SPEED_LIMIT_MAX=2
};

/* /proc/sys/dev/raid */

2002-10-10 20:17:42

by Kevin Corry

[permalink] [raw]
Subject: [PATCH] EVMS core (4/9) passthru.c

Greetings,

Part 4 of the EVMS core driver.

This file provides the Passthru pseudo-plugin, which simply recognizes
EVMS volume labels. It is always bound to the core driver instead of
being a full, separate plugin.

Kevin Corry
[email protected]
http://evms.sourceforge.net/



diff -Naur linux-2.5.41/drivers/evms/core/passthru.c linux-2.5.41-evms/drivers/evms/core/passthru.c
--- linux-2.5.41/drivers/evms/core/passthru.c Wed Dec 31 18:00:00 1969
+++ linux-2.5.41-evms/drivers/evms/core/passthru.c Thu Oct 10 11:16:58 2002
@@ -0,0 +1,373 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2000
+ *
+ * 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
+ */
+/*
+ * EVMS Volume Passthru Manager
+ *
+ * Passthru is a "special" EVMS plugin. It provides all of the services that
+ * a regular plugin provides (volume discovery/delete, I/O and ioctl handling)
+ * but it must always be loaded with the core. Thus, this plugin is not built
+ * as a separate kernel module (like the regular plugins). Instead, it is bound
+ * to the EVMS core module. It also does not have a module_init() or
+ * module_exit(). Instead, the EVMS core will call Passthru's init/exit
+ * functions manually when the core is loaded/unloaded. Also, since it is not a
+ * separate module, it will not do increments/decrements on the module reference
+ * count.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/config.h>
+#include <linux/bio.h>
+#include <linux/evms.h>
+
+#define LOG_PREFIX "passthru: "
+#define EVMS_PASSTHRU_ID 0
+
+static int passthru_discover(struct list_head *);
+static int passthru_delete(struct evms_logical_node *);
+static void passthru_submit_io(struct evms_logical_node *, struct bio *);
+static int passthru_ioctl(struct evms_logical_node *, struct inode *,
+ struct file *, unsigned int, unsigned long);
+static int passthru_init_io(struct evms_logical_node *, int, u64, u64,
+ void *);
+static int passthru_open(struct evms_logical_node *, struct inode *,
+ struct file *);
+static int passthru_release(struct evms_logical_node *, struct inode *,
+ struct file *);
+static int passthru_check_media_change(struct evms_logical_node *, kdev_t);
+static int passthru_revalidate(struct evms_logical_node *, kdev_t);
+static int passthru_quiesce(struct evms_logical_node *, int);
+static int passthru_get_geo(struct evms_logical_node *, u64 *, uint *,
+ uint *, u64 *);
+static int passthru_device_list(struct evms_logical_node *,
+ struct list_head *);
+static int passthru_device_status(struct evms_logical_node *, int *);
+
+static struct evms_plugin_fops fops = {
+ .discover = passthru_discover,
+ .delete = passthru_delete,
+ .submit_io = passthru_submit_io,
+ .sync_io = passthru_init_io,
+ .ioctl = passthru_ioctl,
+ .open = passthru_open,
+ .release = passthru_release,
+ .check_media_change = passthru_check_media_change,
+ .revalidate = passthru_revalidate,
+ .quiesce = passthru_quiesce,
+ .get_geo = passthru_get_geo,
+ .device_list = passthru_device_list,
+ .device_status = passthru_device_status
+};
+
+static struct evms_plugin_header plugin_header = {
+ .id = SetPluginID(IBM_OEM_ID,
+ EVMS_FEATURE,
+ EVMS_PASSTHRU_ID),
+ .version = {
+ .major = 1,
+ .minor = 1,
+ .patchlevel = 1
+ },
+ .fops = &fops
+};
+
+/*******************************/
+/* Discovery support functions */
+/*******************************/
+
+static int
+process_passthru_data(struct evms_logical_node **pp)
+{
+ int rc, size_in_sectors;
+ struct evms_logical_node *node, *new_node;
+
+ node = *pp;
+
+ size_in_sectors =
+ evms_cs_size_in_vsectors(sizeof (struct evms_feature_header));
+
+ /* allocate "parent" node */
+ rc = evms_cs_allocate_logical_node(&new_node);
+ if (rc) {
+ /* on any fatal error, delete the node */
+ int rc2 = DELETE(node);
+ if (rc2) {
+ LOG_DEFAULT("error(%d) attempting to delete node(%s)\n",
+ rc2, node->name);
+ }
+ return rc;
+ }
+ /* initialize "parent" node */
+ new_node->private = node;
+ new_node->flags = node->flags;
+ new_node->plugin = &plugin_header;
+ new_node->system_id = node->system_id;
+ new_node->block_size = node->block_size;
+ new_node->hardsector_size = node->hardsector_size;
+ new_node->total_vsectors = node->total_vsectors;
+ new_node->total_vsectors -= (size_in_sectors << 1) +
+ node->feature_header->alignment_padding;
+ new_node->volume_info = node->volume_info;
+ strcpy(new_node->name, node->name);
+ if (strlen(node->feature_header->object_name))
+ strcat(new_node->name,
+ node->feature_header->object_name);
+ else
+ strcat(new_node->name, "_Passthru");
+ /* return "parent" node to caller */
+ *pp = new_node;
+
+ LOG_DETAILS("feature header found on '%s', created '%s'.\n",
+ node->name, new_node->name);
+ /* we're done with the passthru feature headers
+ * so lets delete them now.
+ */
+ kfree(node->feature_header);
+ node->feature_header = NULL;
+ return 0;
+}
+
+/********** Required Plugin Functions **********/
+
+/**
+ * passthru_discover
+ **/
+static int
+passthru_discover(struct list_head *discover_list)
+{
+ struct evms_logical_node *node, *next_node;
+ struct list_head tmp_list_head;
+
+ INIT_LIST_HEAD(&tmp_list_head);
+ list_splice_init(discover_list, &tmp_list_head);
+ list_for_each_entry_safe(node, next_node, &tmp_list_head, discover) {
+ list_del_init(&node->discover);
+ if (!process_passthru_data(&node)) {
+ if (node) {
+ list_add_tail(&node->discover, discover_list);
+ }
+ }
+ }
+ return 0;
+}
+
+/**
+ * passthru_delete
+ **/
+static int
+passthru_delete(struct evms_logical_node *node)
+{
+ int rc;
+ struct evms_logical_node *p;
+
+ LOG_DETAILS("deleting '%s'.\n", node->name);
+
+ p = node->private;
+ rc = DELETE(p);
+ if (!rc) {
+ evms_cs_deallocate_logical_node(node);
+ } else {
+ LOG_DEFAULT("error(%d) attempting to delete node(%s).\n",
+ rc, node->name);
+ }
+ return rc;
+}
+
+/**
+ * passthru_submit_io
+ **/
+static void
+passthru_submit_io(struct evms_logical_node *node, struct bio *bio)
+{
+ if (bio->bi_sector + bio_sectors(bio) <= node->total_vsectors) {
+ SUBMIT_IO((struct evms_logical_node *) (node->private), bio);
+ } else {
+ LOG_SERIOUS("I/O request on (%s) at rsector ("PFU64") "
+ "beyond boundary ("PFU64").\n",
+ node->name, node->total_vsectors - 1,
+ (u64) bio->bi_sector);
+ bio_io_error(bio, bio->bi_size);
+ }
+}
+
+/**
+ * passthru_ioctl
+ **/
+static int
+passthru_ioctl(struct evms_logical_node *node, struct inode *inode,
+ struct file *file, unsigned int cmd, unsigned long arg)
+{
+ return IOCTL(((struct evms_logical_node *) (node->private)),
+ inode, file, cmd, arg);
+}
+
+/**
+ * passthru_init_io
+ **/
+static int
+passthru_init_io(struct evms_logical_node *node, int rw,
+ u64 sect_nr, u64 num_sects, void *buf_addr)
+{
+ if (sect_nr + num_sects > node->total_vsectors) {
+ return -EINVAL;
+ }
+
+ return INIT_IO(((struct evms_logical_node *) (node->private)),
+ rw, sect_nr, num_sects, buf_addr);
+}
+
+/**
+ * passthru_open - route operation to node
+ * @node: target node
+ * @inode: VFS required parameter
+ * @file: VFS required parameter
+ *
+ * returns:
+ * 0 on success
+ * error code otherwise
+ */
+static int
+passthru_open(struct evms_logical_node *node,
+ struct inode *inode, struct file *file)
+{
+ return OPEN((struct evms_logical_node *)node->private, inode, file);
+}
+
+/**
+ * passthru_release - route operation to node
+ * @node: target node
+ * @inode: VFS required parameter
+ * @file: VFS required parameter
+ *
+ * returns:
+ * 0 on success
+ * error code otherwise
+ */
+static int
+passthru_release(struct evms_logical_node *node,
+ struct inode *inode, struct file *file)
+{
+ return CLOSE((struct evms_logical_node *)node->private, inode, file);
+}
+
+/**
+ * passthru_check_media_change - route operation to node
+ * @node: target node
+ * @dev: VFS required parameter
+ *
+ * returns:
+ * 0 on no change detected,
+ * 1 on change detected,
+ * error code otherwise
+ */
+static int
+passthru_check_media_change(struct evms_logical_node *node, kdev_t dev)
+{
+ return CHECK_MEDIA_CHANGE((struct evms_logical_node *)node->private, dev);
+}
+
+/**
+ * passthru_revalidate - route operation to node
+ * @node: target node
+ * @dev: VFS required parameter
+ *
+ * returns:
+ * 0 on success
+ * error code otherwise
+ */
+static int
+passthru_revalidate(struct evms_logical_node *node, kdev_t dev)
+{
+ return REVALIDATE((struct evms_logical_node *)node->private, dev);
+}
+
+/**
+ * passthru_quiesce - route operation to node
+ * @node: target node
+ * @dev: VFS required parameter
+ *
+ * returns:
+ * 0 on success
+ * error code otherwise
+ */
+static int
+passthru_quiesce(struct evms_logical_node *node, int command)
+{
+ return QUIESCE((struct evms_logical_node *)node->private, command);
+}
+
+/**
+ * passthru_get_geo - route operation to node
+ * @node: target node
+ * @dev: VFS required parameter
+ *
+ * returns:
+ * 0 on success
+ * error code otherwise
+ */
+static int
+passthru_get_geo(struct evms_logical_node *node, u64 *cylinders,
+ uint *heads, uint *sects, u64 *start)
+{
+ return GET_GEO((struct evms_logical_node *)node->private,
+ cylinders, heads, sects, start);
+}
+
+/**
+ * passthru_device_list - route operation to node
+ * @node: target node
+ * @dev: VFS required parameter
+ *
+ * returns:
+ * 0 on success
+ * error code otherwise
+ */
+static int
+passthru_device_list(struct evms_logical_node *node, struct list_head *list)
+{
+ return DEVICE_LIST((struct evms_logical_node *)node->private, list);
+}
+
+/**
+ * passthru_device_status - route operation to node
+ * @node: target node
+ * @dev: VFS required parameter
+ *
+ * returns:
+ * 0 on success
+ * error code otherwise
+ */
+static int
+passthru_device_status(struct evms_logical_node *node, int *status)
+{
+ return DEVICE_STATUS((struct evms_logical_node *)node->private,
+ status);
+}
+
+int
+evms_passthru_init(void)
+{
+ return evms_cs_register_plugin(&plugin_header);
+}
+
+void
+evms_passthru_exit(void)
+{
+ evms_cs_unregister_plugin(&plugin_header);
+}
+

2002-10-10 20:08:56

by Kevin Corry

[permalink] [raw]
Subject: [PATCH] EVMS core (3/9) discover.c

Greetings,

Part 3 of the EVMS core driver.

This file provides the code to coordinate volume
discovery among the EVMS plugins.

Kevin Corry
[email protected]
http://evms.sourceforge.net/



diff -Naur linux-2.5.41/drivers/evms/core/discover.c linux-2.5.41-evms/drivers/evms/core/discover.c
--- linux-2.5.41/drivers/evms/core/discover.c Wed Dec 31 18:00:00 1969
+++ linux-2.5.41-evms/drivers/evms/core/discover.c Thu Oct 10 13:14:27 2002
@@ -0,0 +1,1714 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2000 - 2002
+ *
+ * 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
+ */
+/*
+ * Core routines related to volume discovery and activation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/blk.h>
+#include <linux/root_dev.h>
+#include <linux/evms.h>
+#include <linux/evms_ioctl.h>
+#include "evms_core.h"
+
+/**
+ * evms_discover_logical_disk - invokes the discover code in each device manager plugin
+ * @disk_list: ptr to logical disk (storage objects)
+ *
+ * Traverses the list of plugins, passing the disk_list to each
+ * device manager plugin type.
+ **/
+void
+evms_discover_logical_disks(struct list_head *disk_list)
+{
+ struct evms_plugin_header *plugin;
+ LOG_EXTRA("discovering logical disks...\n");
+ spin_lock(&plugin_lock);
+ list_for_each_entry(plugin, &plugin_head, headers) {
+ if (GetPluginType(plugin->id) == EVMS_DEVICE_MANAGER) {
+ spin_unlock(&plugin_lock);
+ DISCOVER(plugin, disk_list);
+ spin_lock(&plugin_lock);
+ }
+ }
+ spin_unlock(&plugin_lock);
+}
+
+/**
+ * evms_discover_segments - invokes the discover code in each segment manager plugin
+ * @discover_list: ptr to discover list of storage objects
+ *
+ * Traverses the list of plugins, passing the discover list (list of objects
+ * to be probed) to each segment manager plugin type.
+ *
+ * Plugins claim storage objects by removing them from the discover list.
+ * Objects not claimed are left on the list. Newly created objects are put onto
+ * the list. The segment managers return a positive value indicating the number
+ * of object they put onto the list.
+ *
+ * Since segment managers can be stacked, when a segment manager puts a new
+ * object on the list it also returns a count to this function. If this
+ * function receives a positive count from segment manager, it must rerun
+ * through the entire list of segment managers again to allow each segment
+ * manager to probe the new object(s). Rerunning continues until no segment
+ * manager puts any new objects onto the list.
+ *
+ * Plugins with incomplete objects do not put them on the discover list when
+ * invoked through their discover entry point. However, when the plugin's
+ * end_discover entry point is called, they can then put partial volumes (in
+ * READ-ONLY mode) on to the list.
+ **/
+static void
+evms_discover_segments(struct list_head *discover_list)
+{
+ int rc, done;
+
+ struct evms_plugin_header *plugin;
+ LOG_EXTRA("discovering segments...\n");
+ do {
+ done = 1;
+ spin_lock(&plugin_lock);
+ list_for_each_entry(plugin, &plugin_head, headers) {
+ if (GetPluginType(plugin->id) == EVMS_SEGMENT_MANAGER) {
+ spin_unlock(&plugin_lock);
+ rc = DISCOVER(plugin, discover_list);
+ spin_lock(&plugin_lock);
+ if (rc > 0)
+ done = 0;
+ }
+ }
+ spin_unlock(&plugin_lock);
+ } while (done == 0);
+
+ spin_lock(&plugin_lock);
+ list_for_each_entry(plugin, &plugin_head, headers) {
+ if (GetPluginType(plugin->id) == EVMS_SEGMENT_MANAGER) {
+ if (plugin->fops->end_discover) {
+ spin_unlock(&plugin_lock);
+ END_DISCOVER(plugin, discover_list);
+ spin_lock(&plugin_lock);
+ }
+ }
+ }
+ spin_unlock(&plugin_lock);
+}
+
+/**
+ * evms_discover_regions - invokes the discover code in each region manager plugin
+ * @dicover_list: ptr to discover list of storage objects
+ *
+ * Traverses the list of plugins, passing the discover list (list of objects
+ * to be probed) to each region manager plugin type.
+ *
+ * Plugins claim storage objects by removing them from the discover list.
+ * Objects not claimed are left on the list. Newly created objects are put onto
+ * the list. The region managers return a positive value indicating the number
+ * of object they put onto the list.
+ *
+ * Since region managers can be stacked, when a region manager puts a new object
+ * on the list it also returns a count to this function. If this function
+ * receives a positive count from region manager, it must rerun through the
+ * entire list of region managers again to allow each region manager to probe
+ * the new object(s). Rerunning continues until no region manager puts any new
+ * objects onto the list.
+ *
+ * Plugins with incomplete objects do not put them on the discover list when
+ * invoked through their discover entry point. However, when the plugin's
+ * end_discover entry point is called, they can then put partial volumes (in
+ * READ-ONLY mode) on to the list.
+ **/
+static void
+evms_discover_regions(struct list_head *discover_list)
+{
+ int rc, done;
+
+ struct evms_plugin_header *plugin;
+ LOG_EXTRA("discovering regions...\n");
+ do {
+ done = 1;
+ spin_lock(&plugin_lock);
+ list_for_each_entry(plugin, &plugin_head, headers) {
+ if (GetPluginType(plugin->id) == EVMS_REGION_MANAGER) {
+ spin_unlock(&plugin_lock);
+ rc = DISCOVER(plugin, discover_list);
+ spin_lock(&plugin_lock);
+ if (rc > 0)
+ done = 0;
+ }
+ }
+ spin_unlock(&plugin_lock);
+ } while (done == 0);
+
+ spin_lock(&plugin_lock);
+ list_for_each_entry(plugin, &plugin_head, headers) {
+ if (GetPluginType(plugin->id) == EVMS_REGION_MANAGER) {
+ if (plugin->fops->end_discover) {
+ spin_unlock(&plugin_lock);
+ END_DISCOVER(plugin, discover_list);
+ spin_lock(&plugin_lock);
+ }
+ }
+ }
+ spin_unlock(&plugin_lock);
+}
+
+/**
+ * le_feature_header_to_cpu - convert a feature header struct to native cpu format
+ * @fh: feature header ptr
+ *
+ * Convert all the feature header fields into cpu native format from the
+ * on-disk Little Endian format. From this point forward all plugins can deal
+ * with feature headers natively.
+ **/
+void
+le_feature_header_to_cpu(struct evms_feature_header *fh)
+{
+ fh->signature = le32_to_cpup(&fh->signature);
+ fh->crc = le32_to_cpup(&fh->crc);
+ fh->version.major = le32_to_cpup(&fh->version.major);
+ fh->version.minor = le32_to_cpup(&fh->version.minor);
+ fh->version.patchlevel = le32_to_cpup(&fh->version.patchlevel);
+ fh->engine_version.major = le32_to_cpup(&fh->engine_version.major);
+ fh->engine_version.minor = le32_to_cpup(&fh->engine_version.minor);
+ fh->engine_version.patchlevel =
+ le32_to_cpup(&fh->engine_version.patchlevel);
+ fh->flags = le32_to_cpup(&fh->flags);
+ fh->feature_id = le32_to_cpup(&fh->feature_id);
+ fh->sequence_number = le64_to_cpup(&fh->sequence_number);
+ fh->alignment_padding = le64_to_cpup(&fh->alignment_padding);
+ fh->feature_data1_start_lsn =
+ le64_to_cpup(&fh->feature_data1_start_lsn);
+ fh->feature_data1_size = le64_to_cpup(&fh->feature_data1_size);
+ fh->feature_data2_start_lsn =
+ le64_to_cpup(&fh->feature_data2_start_lsn);
+ fh->feature_data2_size = le64_to_cpup(&fh->feature_data2_size);
+ fh->volume_serial_number = le64_to_cpup(&fh->volume_serial_number);
+ fh->volume_system_id = le32_to_cpup(&fh->volume_system_id);
+ fh->object_depth = le32_to_cpup(&fh->object_depth);
+}
+
+/**
+ * edef_load_feature_header - load a feature header into memory
+ * @node: storage object to feature header is read
+ *
+ * load and validate the feature header for a storage object. validation consists
+ * of verifying the signature and crc of the structure. since there is redundant (two)
+ * copies of the feature header, check them both, and select the most recent copy.
+ *
+ * returns: 0 = success
+ * otherwise error code
+ **/
+static int
+edef_load_feature_header(struct evms_logical_node *node)
+{
+ int i, rc = 0, rc_array[2] = { 0, 0 };
+ ulong size_in_bytes;
+ u64 size_in_sectors, starting_sector = 0;
+ struct evms_feature_header *fh = NULL, *fh1 = NULL, *fh2 = NULL;
+ char *location_name = NULL;
+ struct evms_version version = {
+ EVMS_FEATURE_HEADER_MAJOR,
+ EVMS_FEATURE_HEADER_MINOR,
+ EVMS_FEATURE_HEADER_PATCHLEVEL
+ };
+
+ if (node->feature_header) {
+ return 0;
+ }
+ /* allocate buffers for feature headers */
+ size_in_sectors = evms_cs_size_in_vsectors(sizeof (*fh));
+ size_in_bytes = size_in_sectors << EVMS_VSECTOR_SIZE_SHIFT;
+ fh1 = kmalloc(size_in_bytes, GFP_KERNEL);
+ if (!fh1) {
+ return -ENOMEM;
+ }
+ fh2 = kmalloc(size_in_bytes, GFP_KERNEL);
+ if (!fh2) {
+ kfree(fh1);
+ return -ENOMEM;
+ }
+ for (i = 0; i < 2; i++) {
+ if (i == 0) {
+ starting_sector =
+ node->total_vsectors - size_in_sectors;
+ fh = fh1;
+ location_name = EVMS_PRIMARY_STRING;
+ } else {
+ starting_sector--;
+ fh = fh2;
+ location_name = EVMS_SECONDARY_STRING;
+ }
+ /* read header into buffer */
+ rc = INIT_IO(node, 0, starting_sector, size_in_sectors, fh);
+ if (rc) {
+ LOG_ERROR("error(%d) probing for %s feature header "
+ "(at "PFU64") on '%s'.\n", rc, location_name,
+ starting_sector, node->name);
+ rc_array[i] = rc;
+ continue;
+ }
+ /* validate header signature */
+ if (cpu_to_le32(fh->signature) != EVMS_FEATURE_HEADER_SIGNATURE) {
+ rc_array[i] = -ENODATA;
+ continue;
+ }
+ /* validate header CRC */
+ if (fh->crc != EVMS_MAGIC_CRC) {
+ u32 org_crc, final_crc;
+ org_crc = cpu_to_le32(fh->crc);
+ fh->crc = 0;
+ final_crc =
+ evms_cs_calculate_crc(EVMS_INITIAL_CRC, fh,
+ sizeof (*fh));
+ if (final_crc != org_crc) {
+ LOG_ERROR("CRC mismatch error [stored(%x), "
+ "computed(%x)] in %s feature header "
+ "(at "PFU64") on '%s'.\n",
+ org_crc, final_crc, location_name,
+ starting_sector, node->name);
+ rc_array[i] = -EINVAL;
+ continue;
+ }
+ } else {
+ LOG_WARNING("CRC disabled in %s feature header "
+ "(at "PFU64") on '%s'.\n", location_name,
+ starting_sector, node->name);
+ }
+ /* convert the feature header from the
+ * on-disk format (Little Endian) to
+ * native cpu format.
+ */
+ le_feature_header_to_cpu(fh);
+ /* verify the system data version */
+ rc = evms_cs_check_version(&version, &fh->version);
+ if (rc) {
+ LOG_ERROR("error: obsolete version(%d,%d,%d) in %s "
+ "feature header on '%s'.\n",
+ fh->version.major, fh->version.minor,
+ fh->version.patchlevel, location_name,
+ node->name);
+ rc_array[i] = rc;
+ }
+ }
+ /* analyze return codes from both copies */
+ /* getting same return code for both copies? */
+ if (rc_array[0] == rc_array[1]) {
+ rc = rc_array[0];
+ /* if no errors on both copies,
+ * check the sequence numbers.
+ * use the highest sequence number.
+ */
+ if (!rc) {
+ /* compare sequence numbers */
+ if (fh1->sequence_number == fh2->sequence_number) {
+ fh = fh1;
+ } else {
+ LOG_WARNING("%s feature header sequence number"
+ "("PFU64") mismatches %s feature "
+ "header sequence number("PFU64") "
+ "on '%s'!\n", EVMS_PRIMARY_STRING,
+ fh1->sequence_number,
+ EVMS_SECONDARY_STRING,
+ fh2->sequence_number, node->name);
+ if (fh1->sequence_number > fh2->sequence_number) {
+ fh = fh1;
+ location_name = EVMS_PRIMARY_STRING;
+ /* indicate bad sequence number of secondary */
+ rc_array[1] = -1;
+ } else {
+ fh = fh2;
+ location_name = EVMS_SECONDARY_STRING;
+ /* indicate bad sequence number of primary */
+ rc_array[0] = -1;
+ }
+ }
+ }
+ } else /* getting different return codes for each copy */
+ /* if either primary or secondary copy is
+ * valid, use the valid copy.
+ */ if ((rc_array[0] == 0) || (rc_array[1] == 0)) {
+ char *warn_name = NULL;
+
+ /* indicate success */
+ rc = 0;
+ /* set variables based on which copy is valid */
+ if (rc_array[0] == 0) {
+ /* use primary (rear) copy if its good */
+ fh = fh1;
+ location_name = EVMS_PRIMARY_STRING;
+ warn_name = EVMS_SECONDARY_STRING;
+ } else {
+ /* use secondary (front) copy if its good */
+ fh = fh2;
+ location_name = EVMS_SECONDARY_STRING;
+ warn_name = EVMS_PRIMARY_STRING;
+ }
+ /* warn the user about the invalid copy */
+ LOG_WARNING("warning: error(%d) probing/verifying the %s "
+ "feature header on '%s'.\n",
+ rc_array[0] + rc_array[1], warn_name, node->name);
+ } else
+ /* both copies had a different error,
+ * and one was a fatal error, so
+ * indicate fatal error.
+ */
+ if ((rc_array[0] == -EINVAL) || (rc_array[1] == -EINVAL)) {
+ rc = -EINVAL;
+ }
+ /* on error, set fh to NULL */
+ if (rc) {
+ fh = NULL;
+ }
+ /* deallocate metadata buffers appropriately */
+ if (fh != fh2)
+ kfree(fh2);
+ if (fh != fh1)
+ kfree(fh1);
+ /* save validated feature header pointer */
+ if (!rc) {
+ node->feature_header = fh;
+ if (rc_array[0] != rc_array[1]) {
+ LOG_DETAILS("using %s feature header on '%s'.\n",
+ location_name, node->name);
+ }
+ }
+ /* if no signature found, adjust return code */
+ if (rc == -ENODATA) {
+ rc = 0;
+ LOG_DEBUG("no feature header found on '%s'.\n", node->name);
+ }
+ return rc;
+}
+
+/**
+ * edef_find_first_features - probes potential EVMS bottom-most objects for EVMS metadata.
+ * @discover_list: ptr to list of storage objects to probe
+ *
+ * probe this list of objects to see if any objects have EVMS metadata on them. this
+ * probe occurs after regions discover has been completed and represents the earlier
+ * point at which EVMS features could be found.
+ *
+ * if a storage object is found to have EVMS metadata, we allocate a volume info structure,
+ * which keeps track of the persistant EVMS volume data (Serial #, minor #, name). this
+ * volume info structure is then shared by all storage objects built off/from this storage
+ * object.
+ *
+ * EVMS also has metadata it uses to determine if a region/segment/disk is not to be
+ * exported. if we detect these flags here, we purge the storage object from memory.
+ *
+ * returns: 0 = success
+ * otherwise error code
+ **/
+static int
+edef_find_first_features(struct list_head *discover_list)
+{
+ int rc;
+ struct evms_logical_node *node, *next_node, *tmp_node;
+
+ list_for_each_entry_safe(node, next_node, discover_list, discover) {
+ int found = 0;
+
+ /* check for duplicate pointers
+ * search for the node in evms fbottom list
+ */
+ list_for_each_entry(tmp_node, &evms_fbottom_list, fbottom) {
+ if (tmp_node == node) {
+ found = 1;
+ break;
+ }
+ }
+ /* already present? */
+ if (found) {
+ /* yes, already present */
+ LOG_DETAILS("deleting duplicate reference to '%s'.\n",
+ node->name);
+ /* forgot this node by not adding back
+ * onto the discover list.
+ */
+ list_del_init(&node->discover);
+ continue;
+ }
+ /* load the feature header if present */
+ rc = edef_load_feature_header(node);
+ if (rc) {
+ LOG_ERROR("error(%d): reading feature header on '%s'\n",
+ rc, node->name);
+ list_del_init(&node->discover);
+ DELETE(node);
+ continue;
+ }
+ if (node->feature_header) {
+ /* check for object flag */
+ if (node->feature_header->flags &
+ EVMS_VOLUME_DATA_OBJECT) {
+ LOG_DEFAULT("object detected, deleting '%s'.\n",
+ node->name);
+ list_del_init(&node->discover);
+ DELETE(node);
+ continue;
+ }
+ /* check for stop-data flag */
+ if (node->feature_header->flags & EVMS_VOLUME_DATA_STOP) {
+ LOG_DEFAULT("stop data detected, deleting "
+ "'%s'.\n", node->name);
+ list_del_init(&node->discover);
+ DELETE(node);
+ continue;
+ }
+ /* we have a valid feature header.
+ * initialize appropriate node fields
+ * to indicate this.
+ */
+ node->flags |= EVMS_VOLUME_FLAG;
+ node->iflags |= EVMS_FEATURE_BOTTOM;
+ node->volume_info
+ = kmalloc(sizeof (struct evms_volume_info),
+ GFP_KERNEL);
+ if (!node->volume_info) {
+ LOG_ERROR("error(%d): allocating volume info "
+ "struct, deleting '%s'.\n",
+ -ENOMEM, node->name);
+ list_del_init(&node->discover);
+ DELETE(node);
+ continue;
+ }
+ /* set up volume
+ * info struct
+ */
+ node->volume_info->
+ volume_sn =
+ node->feature_header->volume_serial_number;
+ node->volume_info->
+ volume_minor =
+ node->feature_header->volume_system_id;
+ strcpy(node->volume_info->
+ volume_name, node->feature_header->volume_name);
+ /* register(add) node to the bottom feature list */
+ list_add(&node->fbottom, &evms_fbottom_list);
+ }
+ }
+ return 0;
+}
+
+/**
+ * edef_isolate_nodes_ty_type - move storage objects by type from one list to another
+ * @type: type of node to move (see defines below)
+ * @src_list: source list to search
+ * @trg_list: target list to put matching nodes
+ * @compare32: 32-bit comparison field
+ * @compare64: 64-bit comparison field
+ *
+ * moves storage object nodes by type from one list to another.
+ *
+ * returns: 0 = success
+ * otherwise error code
+ **/
+/**
+ * file @type defines
+ **/
+#define ISOLATE_ASSOCIATIVE_FEATURES 0
+#define ISOLATE_COMPATIBILITY_VOLUMES 1
+#define ISOLATE_EVMS_VOLUMES 2
+#define ISOLATE_EVMS_VOLUME_SERIAL_NUMBER 3
+#define ISOLATE_EVMS_NODES_BY_FEATURE_AND_DEPTH 4
+
+static int
+edef_isolate_nodes_by_type(unsigned int type,
+ struct list_head *src_list,
+ struct list_head *trg_list,
+ u32 compare32, u64 compare64)
+{
+ struct evms_logical_node *node, *next_node;
+ int rc = 0, found_node;
+ struct evms_feature_header *fh = NULL;
+
+ list_for_each_entry_safe(node, next_node, src_list, discover) {
+ if (node->feature_header)
+ fh = node->feature_header;
+ found_node = 0;
+ switch (type) {
+ case ISOLATE_ASSOCIATIVE_FEATURES:
+ if (fh) {
+ if (GetPluginType(fh->feature_id) ==
+ EVMS_ASSOCIATIVE_FEATURE) {
+ found_node = 1;
+ }
+ }
+ break;
+ case ISOLATE_COMPATIBILITY_VOLUMES:
+ if (!(node->flags & EVMS_VOLUME_FLAG)) {
+ found_node = 1;
+ }
+ break;
+ case ISOLATE_EVMS_VOLUMES:
+ if (node->flags & EVMS_VOLUME_FLAG) {
+ found_node = 1;
+ }
+ break;
+ /* EVMS volumes with same serial # */
+ case ISOLATE_EVMS_VOLUME_SERIAL_NUMBER:
+ if (node->volume_info->volume_sn == compare64) {
+ found_node = 1;
+ }
+ break;
+ case ISOLATE_EVMS_NODES_BY_FEATURE_AND_DEPTH:
+ if (fh) {
+ if (fh->object_depth == compare64) {
+ if (fh->feature_id == compare32) {
+ found_node = 1;
+ }
+ }
+ }
+ break;
+ }
+ if (found_node == 1) {
+ list_del_init(&node->discover);
+ list_add(&node->discover, trg_list);
+ }
+ }
+ return rc;
+}
+
+/**
+ * edef_apply_feature - apply a feature to a list of objects
+ * @node: object whose plugin's discover function will be invoked
+ * @volume_node_list: list of potentially associated objects used during discover
+ *
+ * invoke the node's plugin's discover entry point and pass in the list of objects
+ * that can be processed by this plugin.
+ *
+ * returns: 0 = success
+ * otherwise error code
+ **/
+static int
+edef_apply_feature(struct evms_logical_node *node,
+ struct list_head *volume_node_list)
+{
+ struct evms_plugin_header *plugin;
+ spin_lock(&plugin_lock);
+ list_for_each_entry(plugin, &plugin_head, headers) {
+ if (plugin->id == node->feature_header->feature_id) {
+ spin_unlock(&plugin_lock);
+ return DISCOVER(plugin, volume_node_list);
+ }
+ }
+ spin_unlock(&plugin_lock);
+ return -ENOPKG;
+}
+
+/**
+ * edef_get_feature_plugin_header - retrieves the header record for a plugin
+ * @id: the feature or plugin ID
+ * @header: the returned plugin header record
+ *
+ * retrieve the header record for the specified plugin.
+ *
+ * returns: 0 = success
+ * otherwise error code
+ **/
+static int
+edef_get_feature_plugin_header(u32 id, struct evms_plugin_header **header)
+{
+ struct evms_plugin_header *plugin;
+ spin_lock(&plugin_lock);
+ list_for_each_entry(plugin, &plugin_head, headers) {
+ if (plugin->id == id) {
+ spin_unlock(&plugin_lock);
+ *header = plugin;
+ return 0;
+ }
+ }
+ spin_unlock(&plugin_lock);
+ LOG_SERIOUS("no plugin loaded for feature id(0x%x)\n", id);
+ return -ENOPKG;
+}
+
+/**
+ * struct evms_volume_build_info - handy struct used during volume create time
+ * @node_count: count of objects in this pass
+ * @feature_count: count of unique plugins for the objects in this pass
+ * @associative_feature_count: count of objects needing associations to continue
+ * @max_depth: object with max volume depth on this pass
+ * @plugin: plugin the max depth object requires to continue
+ * @feature_node_list: list of nodes at max depth requiring @plugin
+ *
+ * handy structure used to track volume building info. updated and reused on each pass.
+ **/
+struct evms_volume_build_info {
+ int node_count;
+ int feature_count;
+ int associative_feature_count;
+ u64 max_depth;
+ struct evms_plugin_header *plugin;
+ struct list_head feature_node_list;
+};
+
+/**
+ * edef_evaluate_volume_node_list - performs one pass on the volume node list
+ * @volume_node_list: list of nodes to built into a volume
+ * @vbi: per pass volume build info
+ * @volume_complete: status flag
+ *
+ * does:
+ * 1) put all nodes from feature list back on volume list
+ * 2) loads the node's feature headers
+ * 3) counts the node list's entries
+ * 4) builds the feature node list
+ * 5) counts the feature headers for associative features
+ * 6) sets feature count to >1 if >1 features to be processed
+ *
+ * returns: 0 = success
+ * otherwise error code
+ */
+static int
+edef_evaluate_volume_node_list(struct list_head *volume_node_list,
+ struct evms_volume_build_info *vbi,
+ int volume_complete)
+{
+ int rc;
+ struct evms_logical_node *node;
+
+ vbi->node_count =
+ vbi->feature_count =
+ vbi->associative_feature_count = vbi->max_depth = 0;
+ vbi->plugin = NULL;
+
+ /* put all feature nodes back on the volume list */
+ rc = edef_isolate_nodes_by_type(ISOLATE_EVMS_VOLUMES,
+ &vbi->feature_node_list,
+ volume_node_list, 0, 0);
+ if (rc) {
+ return rc;
+ }
+ /* load all the feature headers */
+ if (!volume_complete) {
+ list_for_each_entry(node, volume_node_list, discover) {
+ rc = edef_load_feature_header(node);
+ if (rc) {
+ return rc;
+ }
+ }
+ }
+
+ /* find the 1st max depth object:
+ * record the depth
+ * record the plugin
+ */
+ list_for_each_entry(node, volume_node_list, discover) {
+ struct evms_plugin_header *plugin;
+ struct evms_feature_header *fh = node->feature_header;
+
+ /* count the nodes */
+ vbi->node_count++;
+
+ /* no feature header found, continue to next node */
+ if (!fh) {
+ continue;
+ }
+ /* check the depth */
+ if (fh->object_depth > vbi->max_depth) {
+ /* record new max depth */
+ vbi->max_depth = fh->object_depth;
+ /* find the plugin header for this feature id */
+ rc = edef_get_feature_plugin_header(fh->feature_id,
+ &plugin);
+ if (rc) {
+ return rc;
+ }
+ /* check for >1 plugins */
+ if (vbi->plugin != plugin) {
+ vbi->feature_count++;
+ vbi->plugin = plugin;
+ }
+ }
+ /* check for "associative" feature indicator */
+ if (GetPluginType(vbi->plugin->id) == EVMS_ASSOCIATIVE_FEATURE) {
+ vbi->associative_feature_count++;
+ }
+ }
+ /* build a list of max depth nodes for this feature */
+ if (vbi->max_depth) {
+ rc = edef_isolate_nodes_by_type
+ (ISOLATE_EVMS_NODES_BY_FEATURE_AND_DEPTH, volume_node_list,
+ &vbi->feature_node_list, vbi->plugin->id, vbi->max_depth);
+ if (rc) {
+ return rc;
+ }
+ if (!vbi->plugin) {
+ return -ENODATA;
+ }
+ if (list_empty(&vbi->feature_node_list)) {
+ return -ENODATA;
+ }
+ }
+ return rc;
+}
+
+/**
+ * edef_check_feature_conditions
+ * @vbi: volume build info
+ *
+ * This routine verifies the state of volume based on the features
+ * headers and nodes in the current @vbi.
+ */
+static int
+edef_check_feature_conditions(struct evms_volume_build_info *vbi)
+{
+ if (vbi->associative_feature_count) {
+ if (vbi->node_count > 1) {
+ LOG_ERROR("associative ERROR: > 1 nodes(%d) remaining "
+ "to be processed!\n", vbi->node_count);
+ return -EVMS_VOLUME_FATAL_ERROR;
+ }
+ if (vbi->max_depth != 1) {
+ LOG_ERROR("associative ERROR: associative feature "
+ "found at node depth("PFU64") != 1!\n",
+ vbi->max_depth);
+ return -EVMS_VOLUME_FATAL_ERROR;
+ }
+ return -EVMS_ASSOCIATIVE_FEATURE;
+ }
+ if (!vbi->max_depth) {
+ if (vbi->node_count > 1) {
+ LOG_ERROR("max depth ERROR: > 1 nodes(%d) remaining "
+ "to be processed!\n", vbi->node_count);
+ return -EVMS_VOLUME_FATAL_ERROR;
+ }
+ }
+ if (vbi->max_depth == 1) {
+ if (vbi->feature_count > 1) {
+ LOG_ERROR("max depth 1 ERROR: > 1 features remaining "
+ "to be processed!\n");
+ return -EVMS_VOLUME_FATAL_ERROR;
+ }
+ }
+ return 0;
+}
+
+/** edef_apply_features
+ * @volume_node_list: all the objects to be built into a volume
+ *
+ * This routine applies none, one, or more features to an EVMS
+ * volume. The features are applied and verified recursively until the
+ * entire volume has been constructed. Fatal errors result in
+ * all nodes in the volume discovery list being deleted.
+ *
+ * returns: 0 = success
+ * otherwise error code
+ */
+static int
+edef_apply_features(struct list_head *volume_node_list)
+{
+ int rc, done, top_feature_applying;
+ struct evms_volume_build_info vbi;
+
+ INIT_LIST_HEAD(&vbi.feature_node_list);
+ rc = edef_evaluate_volume_node_list(volume_node_list, &vbi, 0);
+
+ /* ensure we don't go into the next loop
+ * without having a target plugin to
+ * pass control to.
+ */
+ if (!rc) {
+ if (!vbi.plugin) {
+ rc = -ENODATA;
+ }
+ }
+
+ /* this loop should ONLY get used when
+ * there are features to process.
+ */
+ done = (rc) ? 1 : 0;
+ while (!done) {
+ rc = edef_check_feature_conditions(&vbi);
+ if (rc)
+ break;
+ top_feature_applying = (vbi.max_depth == 1) ? 1 : 0;
+ rc = vbi.plugin->fops->discover(&vbi.feature_node_list);
+ if (!rc) {
+ rc = edef_evaluate_volume_node_list(volume_node_list,
+ &vbi,
+ top_feature_applying);
+ if (top_feature_applying == 1) {
+ if (vbi.node_count > 1) {
+ rc = -EVMS_VOLUME_FATAL_ERROR;
+ LOG_ERROR("ERROR: detected > 1 node at "
+ "volume completion!\n");
+ }
+ done = 1;
+ } else {
+ if (!vbi.plugin) {
+ rc = -EVMS_VOLUME_FATAL_ERROR;
+ LOG_ERROR("ERROR: depth("PFU64"): "
+ "expected another feature!\n",
+ vbi.max_depth);
+ done = 1;
+ }
+ }
+ } else { /* rc != 0 */
+ rc = -EVMS_VOLUME_FATAL_ERROR;
+ done = 1;
+ }
+ }
+ if (rc) {
+ /* put all feature nodes back on the volume list */
+ if (edef_isolate_nodes_by_type(ISOLATE_EVMS_VOLUMES,
+ &vbi.feature_node_list,
+ volume_node_list, 0, 0)) {
+ BUG();
+ }
+ }
+ return rc;
+}
+
+/**
+ * edef_delete_node - generic delete an object from a volume node list
+ * @node_list: the current object list
+ * @node: the object to be deleted
+ * @return_code: the error code
+ * @log_text: logging text
+ *
+ * convenience function to delete an object from a volume list. this generates
+ * the appropriate syslog messages and sets the return code.
+ *
+ * returns: 0 = success
+ * otherwise error code
+ **/
+static int
+edef_delete_node(struct list_head *node_list,
+ struct evms_logical_node *node, int return_code,
+ char *log_text)
+{
+ int rc;
+
+ if (!list_empty(&node->discover)) {
+ list_del_init(&node->discover);
+ LOG_ERROR("%s error(%d): deleting volume(%s), node(%s)\n",
+ log_text, return_code,
+ node->volume_info->volume_name, node->name);
+ rc = DELETE(node);
+ if (rc) {
+ LOG_ERROR("error(%d) while deleting node(%s)\n",
+ rc, node->name);
+ }
+ } else {
+ LOG_WARNING("%s error(%d): node gone, assumed deleted by "
+ "plugin.\n", log_text, return_code);
+ /* plugin must have cleaned up the node.
+ * So just reset the return code and leave.
+ */
+ rc = 0;
+ }
+
+ return rc;
+}
+
+/**
+ * edef_process_evms_volumes - build an EVMS volume from storage objects
+ * @discover_list: list of all objects being discovered
+ * @associative_feature_list: returned list of objects required associative features
+ *
+ * groups all evms objects with the same serial number onto a volume list and sends
+ * that list into edef_apply_features. this process is repeated unto all evms volume
+ * serial number groups have been processed. objects representing complete volumes
+ * are put back on the discover list and objects requiring associative processing
+ * are put on an associative feature list.
+ *
+ * returns: 0 = success
+ * otherwise error code
+ **/
+static int
+edef_process_evms_volumes(struct list_head *discover_list,
+ struct list_head *associative_feature_list)
+{
+ int rc = 0;
+ struct list_head evms_volumes_list, volume_node_list;
+ struct evms_logical_node *node = NULL;
+ u64 volume_sn;
+
+ INIT_LIST_HEAD(&evms_volumes_list);
+ /* put all EVMS volumes on their own list */
+ rc = edef_isolate_nodes_by_type(ISOLATE_EVMS_VOLUMES,
+ discover_list,
+ &evms_volumes_list, 0, 0);
+
+ /* apply features to each EVMS volume */
+ /* one volume at a time on each pass */
+ while (!list_empty(&evms_volumes_list)) {
+ node = list_entry(evms_volumes_list.next, struct evms_logical_node, discover);
+ /* put all nodes for one EVMS volume on separate list */
+ INIT_LIST_HEAD(&volume_node_list);
+ volume_sn = node->volume_info->volume_sn;
+ rc = edef_isolate_nodes_by_type
+ (ISOLATE_EVMS_VOLUME_SERIAL_NUMBER, &evms_volumes_list,
+ &volume_node_list, 0, volume_sn);
+ if (rc) {
+ break;
+ }
+ /* go apply all the volume features now */
+ rc = edef_apply_features(&volume_node_list);
+ switch (rc) {
+ case 0: /* SUCCESS */
+ /* remove volume just processed */
+ node = list_entry(volume_node_list.next, struct evms_logical_node, discover);
+ list_del_init(&node->discover);
+ /* put volume on global list */
+ list_add_tail(&node->discover, discover_list);
+ break;
+ case -EVMS_ASSOCIATIVE_FEATURE:
+ /* put all "associative" features on their own list */
+ rc = edef_isolate_nodes_by_type
+ (ISOLATE_ASSOCIATIVE_FEATURES, &volume_node_list,
+ associative_feature_list, 0, 0);
+ break;
+ default: /* FATAL ERROR */
+ /* delete each node remaining in the list */
+ if (!list_empty(&volume_node_list)) {
+ node = list_entry(volume_node_list.next, struct evms_logical_node, discover);
+ LOG_ERROR("encountered fatal error building "
+ "volume '%s'\n",
+ node->volume_info->volume_name);
+ }
+ while (!list_empty(&volume_node_list)) {
+ node = list_entry(volume_node_list.next, struct evms_logical_node, discover);
+ edef_delete_node(&volume_node_list,
+ node, rc, "EVMS feature");
+ }
+ rc = 0;
+ break;
+ }
+ if (rc) {
+ break;
+ }
+ }
+ return rc;
+}
+
+/**
+ * edef_process_associative_volumes - find the associative feature for the objects in this list
+ * @associative_feature_list: list of objects requiring associative features
+ * @discover_list: list to put completed volumes object back onto
+ *
+ * find and apply the appropriate associative feature for the objects on the
+ * associative feature list. if successfully applied, put the resulting completed
+ * volume objects on to the discover list. error result in the volume objects
+ * being deleted.
+ *
+ * returns: 0 = success
+ * otherwise error code
+ **/
+static int
+edef_process_associative_volumes(struct list_head *associative_feature_list,
+ struct list_head *discover_list)
+{
+ int rc = 0;
+ struct evms_logical_node *node, *next_node;
+
+ list_for_each_entry_safe(node, next_node, associative_feature_list, discover) {
+ /* remove this node from associative feature list */
+ list_del_init(&node->discover);
+ rc = edef_load_feature_header(node);
+ if (rc) {
+ edef_delete_node(discover_list, node, rc,
+ "Associative Feature");
+ continue;
+ }
+ /* put volume on global list */
+ list_add_tail(&node->discover, discover_list);
+ rc = edef_apply_feature(node, discover_list);
+ }
+ return rc;
+}
+
+/**
+ * edef_check_for_incomplete_volumes
+ * @discover_list: list of post discovery time volume objects
+ *
+ * check to see if any incomplete volumes are left around if so, delete them.
+ * complete volumes should not have feature_headers hanging off them, if we
+ * find any, we know the volume is incomplete.
+ **/
+static void
+edef_check_for_incomplete_volumes(struct list_head *discover_list)
+{
+ struct evms_logical_node *next_node, *node;
+
+ list_for_each_entry_safe(node, next_node, discover_list, discover) {
+ if (node->feature_header) {
+ edef_delete_node(discover_list, node, 0,
+ "Unexpected feature header");
+ }
+ }
+}
+
+/**
+ * evms_discover_evms_features
+ * @discover_list: list of objects to discover evms features on
+ *
+ * find EVMS features for nodes on the discover list
+ *
+ * returns: 0 = success
+ * otherwise error code
+ */
+static int
+evms_discover_evms_features(struct list_head *discover_list)
+{
+ struct list_head associative_feature_list;
+ int rc;
+
+ LOG_EXTRA("discovering evms volume features...\n");
+
+ INIT_LIST_HEAD(&associative_feature_list);
+ rc = edef_find_first_features(discover_list);
+ if (rc) {
+ return rc;
+ }
+ rc = edef_process_evms_volumes(discover_list,
+ &associative_feature_list);
+ if (rc) {
+ return rc;
+ }
+ rc = edef_process_associative_volumes(&associative_feature_list,
+ discover_list);
+ if (rc) {
+ return rc;
+ }
+ edef_check_for_incomplete_volumes(discover_list);
+ return 0;
+}
+
+/**
+ * eelv_geninit - initialize a gendisk entry for a volume
+ * @volume: ptr to new volume
+ * @minor: minor value of new volume
+ *
+ * This routine initializes a gendisk entry for the specified volume.
+ **/
+static void
+eelv_geninit(struct evms_logical_volume *lv)
+{
+ struct gendisk *gd = lv->gd;
+
+ if (!gd) {
+ gd = alloc_disk();
+ BUG_ON(!gd);
+ lv->gd = gd;
+ }
+
+ gd->major = EVMS_MAJOR;
+ gd->first_minor = lv->minor;
+ snprintf(gd->disk_name, 16, "%s%d", EVMS_DIR_NAME, lv->minor);
+ gd->fops = &evms_fops;
+ set_capacity(gd, lv->node->total_vsectors);
+
+ /* set removable flags as needed */
+ gd->flags = 0;
+ if (lv->flags & EVMS_DEVICE_REMOVABLE) {
+ gd->flags |= GENHD_FL_REMOVABLE;
+ }
+
+ gd->de = devfs_register(evms_dir_devfs_handle,
+ lv->name, DEVFS_FL_DEFAULT,
+ EVMS_MAJOR, lv->minor,
+ S_IFBLK | S_IRUGO | S_IWUGO,
+ &evms_fops, NULL);
+
+ add_disk(gd);
+}
+
+/**
+ * add_logical_volume - adds a newly created logical volume to our list
+ * @lv: logical volume to be added.
+ *
+ * adds a newly created logical volume to our list.
+ * this function keeps the list ordered by minor
+ * number from low to high.
+ **/
+static void
+add_logical_volume(struct evms_logical_volume *lv)
+{
+ if (list_empty(&evms_logical_volumes)) {
+ list_add(&lv->volumes, &evms_logical_volumes);
+ } else {
+ struct evms_logical_volume *tmpvol;
+ list_for_each_entry(tmpvol, &evms_logical_volumes, volumes) {
+ BUG_ON(tmpvol->minor == lv->minor);
+ if (tmpvol->minor > lv->minor) {
+ break;
+ }
+ }
+ list_add(&lv->volumes, tmpvol->volumes.prev);
+ }
+}
+
+/**
+ * evms_do_request_fn
+ * @q: request queue
+ **/
+static void
+evms_do_request_fn(request_queue_t * q)
+{
+ LOG_WARNING("This function should not be called.\n");
+}
+
+/**
+ * eelv_assign_volume_minor - assigns a minor number to a volume
+ * @node: volume object to process
+ * @minor: minor device number to assign
+ *
+ * This routine assigns a specific minor number to a volume. It
+ * also performs the remaining steps to make this volume visible
+ * and usable to the kernel.
+ **/
+static void
+eelv_assign_volume_minor(struct evms_logical_node *node, int minor,
+ struct evms_logical_volume *vol)
+{
+ struct evms_logical_volume *lv = vol;
+
+ if (!lv) {
+ lv = kmalloc(sizeof(struct evms_logical_volume), GFP_KERNEL);
+ BUG_ON(!lv);
+ memset(lv, 0, sizeof(struct evms_logical_volume));
+ lv->requests_in_progress = (atomic_t) ATOMIC_INIT(0);
+ }
+ lv->node = node;
+ lv->name = kmalloc(strlen(EVMS_GET_NODE_NAME(node)) + 1, GFP_KERNEL);
+ BUG_ON(!lv->name);
+ strcpy(lv->name, EVMS_GET_NODE_NAME(node));
+ lv->minor = minor;
+ lv->flags = node->flags;
+ if (lv->flags & EVMS_VOLUME_READ_ONLY) {
+ set_device_ro(mk_kdev(EVMS_MAJOR, minor), 1);
+ }
+
+ /* round volume size down to next hardsector */
+ node->total_vsectors &=
+ ~((node->hardsector_size >> EVMS_VSECTOR_SIZE_SHIFT) - 1);
+
+ eelv_geninit(lv);
+
+ /* set up the request queue for this volume */
+ lv->requests_in_progress = (atomic_t) ATOMIC_INIT(0);
+ lv->request_lock = SPIN_LOCK_UNLOCKED;
+ if (!vol) {
+ init_waitqueue_head(&lv->quiesce_wait_queue);
+ init_waitqueue_head(&lv->request_wait_queue);
+ blk_init_queue(&lv->request_queue,
+ evms_do_request_fn,
+ &lv->request_lock);
+ blk_queue_make_request(&lv->request_queue,
+ evms_make_request_fn);
+ add_logical_volume(lv);
+ }
+ evms_volumes++;
+ LOG_DEFAULT("Exporting EVMS Volume(%u,%u) from \"%s%s\".\n",
+ EVMS_MAJOR, minor, EVMS_DIR_NAME "/", lv->name);
+}
+
+/**
+ * eelv_check_for_duplicity
+ * @discover_list: list of potential volume objects to export
+ *
+ * This routine compares the serial number in the top most node
+ * in the volume to the list of currently exported volumes. If
+ * this volumes serial number is found in the list then we know
+ * this volume is a duplicate and it is then delete.
+ **/
+static void
+eelv_check_for_duplicity(struct list_head *discover_list)
+{
+ struct evms_logical_node *next_node, *node;
+
+ list_for_each_entry_safe(node, next_node, discover_list, discover) {
+ struct evms_logical_volume *lv = NULL;
+ int is_dup = 0;
+ while ((lv = find_next_volume(lv))) {
+ char *type_ptr = NULL;
+ /* only check exported volumes */
+ if (!lv->node) {
+ continue;
+ }
+ /* check for duplicate pointer */
+ if (node == lv->node) {
+ is_dup = 1;
+ type_ptr = "pointer";
+ /* check for duplicate node */
+ } else if (!strcmp(node->name, lv->node->name)) {
+ is_dup = 1;
+ type_ptr = "node";
+ }
+ if (is_dup == 1) {
+ LOG_DETAILS("deleting duplicate %s to EVMS "
+ "volume(%u,%u,%s)...\n",
+ type_ptr, EVMS_MAJOR, lv->minor,
+ EVMS_GET_NODE_NAME(node));
+ list_del_init(&node->discover);
+ /* forget duplicate */
+ break;
+ }
+ }
+ }
+}
+
+/**
+ * eelv_reassign_soft_deleted_volume_minors
+ * @discover_list: list of volume objects to export
+ *
+ * This routine reassigns previous minor numbers to rediscovered "soft"
+ * deleted volumes.
+ **/
+static void
+eelv_reassign_soft_deleted_volume_minors(struct list_head *discover_list)
+{
+ struct evms_logical_node *next_node, *node;
+
+ list_for_each_entry_safe(node, next_node, discover_list, discover) {
+ struct evms_logical_volume *lv = NULL;
+ while ((lv = find_next_volume(lv))) {
+ /* only check soft deleted volumes:
+ * they have a non-NULL name.
+ */
+ if (!(lv->flags & EVMS_VOLUME_SOFT_DELETED)) {
+ continue;
+ }
+ if (strcmp(EVMS_GET_NODE_NAME(node), lv->name)) {
+ continue;
+ }
+ /* reassign requested minor */
+ list_del_init(&node->discover);
+ LOG_DEFAULT("Re");
+ /* free the previously used name */
+ kfree(lv->name);
+ lv->name = NULL;
+ /* clear the EVMS_VOLUME_SOFT_DELETED flag */
+ lv->flags = 0;
+ eelv_assign_volume_minor(node, lv->minor, lv);
+ break;
+ }
+ }
+}
+
+/**
+ * eelv_assign_evms_volume_minors
+ * @discover_list: list of volume objects to export
+ *
+ * This routine assigns minor numbers to new evms volumes. If
+ * the specified minor is already in use, the requested minor
+ * is set to 0, and will be assigned next available along with
+ * any remaining volumes at the end of evms_export_logical_volumes.
+ **/
+static void
+eelv_assign_evms_volume_minors(struct list_head *discover_list)
+{
+ struct evms_logical_node *next_node, *node, *lv_node;
+ unsigned int requested_minor, lv_flags;
+
+ list_for_each_entry_safe(node, next_node, discover_list, discover) {
+ /* only process evms volumes */
+ if (!(node->flags & EVMS_VOLUME_FLAG)) {
+ continue;
+ }
+ requested_minor = node->volume_info->volume_minor;
+ /* is there a requested minor? */
+ if (!requested_minor) {
+ continue;
+ }
+ /* check range of requested minor */
+ if (requested_minor >= MAX_EVMS_VOLUMES) {
+ lv_node = node;
+ lv_flags = 0;
+ } else {
+ struct evms_logical_volume *lv;
+ lv_node = NULL;
+ lv_flags = 0;
+ lv = lookup_volume(requested_minor);
+ if (lv) {
+ lv_node = lv->node;
+ lv_flags = lv->flags;
+ }
+ }
+ if (lv_node || (lv_flags & EVMS_VOLUME_SOFT_DELETED)) {
+ LOG_WARNING("EVMS volume(%s) requesting invalid/in-use "
+ "minor(%d), assigning next available!\n",
+ node->volume_info->volume_name,
+ requested_minor);
+ /*
+ * requested minor is already
+ * in use, defer assignment
+ * until later.
+ */
+ node->volume_info->volume_minor = 0;
+ } else {
+ /* assign requested minor */
+ list_del_init(&node->discover);
+ eelv_assign_volume_minor(node, requested_minor, NULL);
+ }
+ }
+}
+
+/**
+ * find_unused_minor - finds the first unused minor based on the specified direction
+ * @dir: direction of search (0 = forward, 1 = backward)
+ *
+ * Searches for the first available minor number in the specified search
+ * direction.
+ *
+ * Returns the first available minor number or 0. 0 is reserved for the block
+ * device itself and is therefore never usable for a logical volume.
+ **/
+static int
+find_unused_minor(int dir)
+{
+ int i;
+ if (!dir) {
+ for (i = 1; i < MAX_EVMS_VOLUMES; i++) {
+ if (!lookup_volume(i)) {
+ return i;
+ }
+ }
+ } else {
+ for (i = MAX_EVMS_VOLUMES - 1; i; i--) {
+ if (!lookup_volume(i)) {
+ return i;
+ }
+ }
+ }
+ return 0;
+}
+
+/**
+ * eelv_assign_remaining_evms_volume_minors
+ * @discover_list: list of volume objects to export
+ *
+ * This routine assigns minor numbers to new evms volumes that
+ * have no/conflicting minor assignments. This function will
+ * search from high(255) minor values down, for the first available
+ * minor. Searching high to low minimizes the possibility of
+ * conflicting evms volumes causing "compatibility" minor
+ * assignments to shift from expected assignments.
+ */
+static void
+eelv_assign_remaining_evms_volume_minors(struct list_head *discover_list)
+{
+ struct evms_logical_node *next_node, *node;
+ int minor;
+
+ list_for_each_entry_safe(node, next_node, discover_list, discover) {
+ /* only process evms volumes */
+ /* all remaining evms volumes should now
+ * have a minor value of 0, meaning they
+ * had no minor assignment, or their minor
+ * assignment conflicted with an existing
+ * minor assignment.
+ */
+ if (!(node->flags & EVMS_VOLUME_FLAG)) {
+ continue;
+ }
+ list_del_init(&node->discover);
+ /* find next available minor number */
+ minor = find_unused_minor(1);
+ /* check range of assigned minor */
+ if (!minor) {
+ LOG_CRITICAL("no more minor numbers available for "
+ "evms volumes!!!!\n");
+ DELETE(node);
+ } else {
+ /* assign requested minor */
+ eelv_assign_volume_minor(node, minor, NULL);
+ }
+ }
+}
+
+/**
+ * eelv_assign_remaining_volume_minors
+ * @discover_list: list of volume objects to export
+ *
+ * This routine assigns minor numbers to all remaining unassigned
+ * volumes. Minor numbers are assigned on an first availability
+ * basis. The first free minor number is used in the assignment.
+ **/
+static void
+eelv_assign_remaining_volume_minors(struct list_head *discover_list)
+{
+ struct evms_logical_node *node, *next_node;
+ int minor;
+
+ list_for_each_entry_safe(node, next_node, discover_list, discover) {
+ list_del_init(&node->discover);
+ /* find next available minor number */
+ minor = find_unused_minor(0);
+ if (!minor) {
+ LOG_CRITICAL("no more minor numbers available for "
+ "compatibility volumes!!!!\n");
+ DELETE(node);
+ } else {
+ /* assign minor */
+ eelv_assign_volume_minor(node, minor, NULL);
+ }
+ }
+}
+
+/**
+ * eelv_check_for_unreassign_soft_deleted_volume
+ * @discover_list: list of volume objects to export
+ *
+ * This routine reports any "soft deleted" volumes that were not
+ * found after a rediscovery.
+ **/
+static void
+eelv_check_for_unreassign_soft_deleted_volume(void)
+{
+ struct evms_logical_volume *lv, *next_lv;
+
+ next_lv = NULL;
+ while ((lv = find_next_volume_safe(&next_lv))) {
+ /* only check soft deleted volumes:
+ * they have a NULL node ptr &
+ * they have a non-NULL name.
+ */
+ if (!(lv->flags & EVMS_VOLUME_SOFT_DELETED)) {
+ continue;
+ }
+ if (is_busy(mk_kdev(EVMS_MAJOR, lv->minor))) {
+ lv->flags |= EVMS_VOLUME_CORRUPT;
+ }
+ LOG_ERROR("error: rediscovery failed to find %smounted "
+ "'soft deleted' volume(%u,%u,%s)...\n",
+ ((lv->flags & EVMS_VOLUME_CORRUPT) ? "" : "un"),
+ EVMS_MAJOR, lv->minor, lv->name);
+ if (lv->flags & EVMS_VOLUME_CORRUPT) {
+ LOG_ERROR(" flagging volume(%u:%u,%s) as CORRUPT!\n",
+ EVMS_MAJOR, lv->minor, lv->name);
+ } else {
+ LOG_ERROR(" releasing minor(%d) used by "
+ "volume(%s)!\n", lv->minor, lv->name);
+ /* clear logical volume structure
+ * for this volume so it may be
+ * reused.
+ */
+ blk_cleanup_queue(&lv->request_queue);
+ kfree(lv->name);
+ list_del_init(&lv->volumes);
+ kfree(lv);
+ }
+ }
+}
+
+/**
+ * eelv_unquiesce_volumes - unquiesces all volumes after a rediscover operation
+ *
+ * unquiesces all volumes after a rediscover operation.
+ **/
+static void
+eelv_unquiesce_volumes(void)
+{
+ struct evms_logical_volume *lv = NULL;
+ /* check each volume entry */
+ while((lv = find_next_volume(lv))) {
+ /* is this volume "quiesced" ? */
+ if (!lv->quiesced) {
+ continue;
+ }
+ if (!lv->node) {
+ continue;
+ }
+ /* "unquiesce" it */
+ evms_quiesce_volume(lv, 0, lv->minor, 0);
+ }
+}
+
+/**
+ * evms_export_logical_volumes
+ *
+ * This function is called from evms_discover_volumes. It
+ * check for duplicate volumes, assigns minor values to evms
+ * volumes, and assigns minor values to the remaining volumes.
+ * In addition to assigning minor values to each volume this
+ * function also completes the final steps necessary to allow
+ * the volumes to be using by the operating system.
+ **/
+static void
+evms_export_logical_volumes(struct list_head *discover_list)
+{
+ LOG_EXTRA("exporting EVMS logical volumes...\n");
+
+ eelv_check_for_duplicity(discover_list);
+ eelv_reassign_soft_deleted_volume_minors(discover_list);
+ eelv_assign_evms_volume_minors(discover_list);
+ eelv_assign_remaining_evms_volume_minors(discover_list);
+ eelv_assign_remaining_volume_minors(discover_list);
+ eelv_check_for_unreassign_soft_deleted_volume();
+ eelv_unquiesce_volumes();
+}
+
+/**
+ * edv_populate_discover_list - initially populates the discover list with disk objects
+ * @src_list: source list for disk objects
+ * @trg_list: target list for disk objects
+ * @discover_parms: rediscover ioctl packet
+ *
+ * based on the presence or the contents of the rediscover packet, the discover list
+ * is populated with disk objects. if the @discover_parms == NULL, all disk objects
+ * will be copied to the discover list. if the @discover_parms points to a rediscover
+ * packet, the disks matching the disk handles from in the ioctl packet will be copied
+ * to the discover list.
+ *
+ **/
+static void
+edv_populate_discover_list(struct list_head *src_list,
+ struct list_head *trg_list,
+ struct evms_rediscover_pkt *discover_parms)
+{
+ int i, use_all_disks = 0;
+ struct evms_logical_node *node;
+
+ /* if no discover parameters are specified */
+ /* copy ALL the disk nodes into the */
+ /* discovery list. */
+ if ((discover_parms == NULL) ||
+ (discover_parms->drive_count == REDISCOVER_ALL_DEVICES))
+ use_all_disks = 1;
+
+ /* copy the disk nodes specified in the */
+ /* discover_parms over to a discover list */
+
+ list_for_each_entry(node, &evms_device_list, device) {
+ int move_node;
+ move_node = use_all_disks;
+ if (move_node == 0)
+ /* check the rediscovery array */
+ for (i = 0; i < discover_parms->drive_count; i++) {
+ struct evms_logical_node *disk_node =
+ DEV_HANDLE_TO_NODE(discover_parms->
+ drive_array[i]);
+ if (disk_node == node) {
+ move_node = 1;
+ break;
+ }
+ }
+ /* check to see if we want this node */
+ if (move_node == 1) {
+ if (list_empty(&node->discover)) {
+ list_add_tail(&node->discover, trg_list);
+ }
+ }
+ }
+}
+
+/**
+ * evms_discover_volumes - probe the disks to discover volumes
+ * @discover_parms: ptr to a rediscover ioctl packet
+ *
+ * perform a rediscover operation. the presence or contents of a rediscover packet
+ * will dictate the scope of the operation.
+ *
+ * returns: 0 = success
+ * otherwise error code
+ **/
+int
+evms_discover_volumes(struct evms_rediscover_pkt *discover_parms)
+{
+ struct list_head discover_list;
+
+ INIT_LIST_HEAD(&discover_list);
+ evms_discover_logical_disks(&discover_list);
+ if (!list_empty(&evms_device_list)) {
+ /* move the appropriate disk nodes, based on */
+ /* on the discover parameters, onto the */
+ /* discover list for the partition managers */
+ /* to process */
+ edv_populate_discover_list(&evms_device_list,
+ &discover_list, discover_parms);
+ }
+ if (!list_empty(&discover_list)) {
+ evms_discover_segments(&discover_list);
+ }
+ if (!list_empty(&discover_list)) {
+ evms_discover_regions(&discover_list);
+ }
+ if (!list_empty(&discover_list)) {
+ evms_discover_evms_features(&discover_list);
+ }
+ if (!list_empty(&discover_list)) {
+ evms_export_logical_volumes(&discover_list);
+ evms_cs_signal_event(EVMS_EVENT_END_OF_DISCOVERY);
+ }
+ BUG_ON(!list_empty(&discover_list));
+ return 0;
+}
+
+/**
+ * find_root_fs_dev
+ *
+ * If "root=/dev/evms/???" was specified on the kernel command line, and devfs
+ * is not enabled, we need to determine the appropriate minor number for the
+ * specified volume for the root fs.
+ *
+ * This function will never get called if EVMS is built as a module, and
+ * adding the #ifndef MODULE condition prevents having to add an
+ * EXPORT_SYMBOL() somewhere for get_root_device_name.
+ */
+static void
+find_root_fs_dev(void)
+{
+#ifndef MODULE
+ struct evms_logical_volume *lv;
+ char root_name[64] = { 0 };
+ char *name;
+
+ //get_root_device_name(root_name);
+
+ if (!strncmp(root_name, EVMS_DIR_NAME "/", strlen(EVMS_DIR_NAME) + 1)) {
+ name = &root_name[strlen(EVMS_DIR_NAME) + 1];
+
+ lv = NULL;
+ while ((lv = find_next_volume(lv))) {
+ if (lv->name &&
+ !strncmp(name, lv->name, strlen(lv->name))) {
+ ROOT_DEV = kdev_val(mk_kdev(EVMS_MAJOR, lv->minor));
+ return;
+ }
+ }
+ }
+#endif
+}
+
+/**
+ * evms_init_discover
+ *
+ * If EVMS is statically built into the kernel, this function will be called
+ * to perform an initial volume discovery.
+ **/
+int __init
+evms_init_discover(void)
+{
+ /* Go find volumes */
+ evms_discover_volumes(NULL);
+
+ /* Check if the root fs is on EVMS */
+ if (MAJOR(ROOT_DEV) == EVMS_MAJOR) {
+ find_root_fs_dev();
+ }
+ return 0;
+}
+
+__initcall(evms_init_discover);
+

2002-10-10 20:42:57

by Andi Kleen

[permalink] [raw]
Subject: Re: [PATCH] EVMS core (3/9) discover.c

Kevin Corry <[email protected]> writes:

> + list_for_each_entry(plugin, &plugin_head, headers) {
> + if (GetPluginType(plugin->id) == EVMS_DEVICE_MANAGER) {
> + spin_unlock(&plugin_lock);
> + DISCOVER(plugin, disk_list);
> + spin_lock(&plugin_lock);
> + }

How do you know "plugin" and its successors are still valid when retaking
the spinlock? Looks like you need a reference count on the object here.

Similar with other functions.

> +
> + if (!gd) {
> + gd = alloc_disk();
> + BUG_ON(!gd);


BUG_ON ? Can't this fail for legal reasons?


-Andi

2002-10-10 21:44:04

by Kevin Corry

[permalink] [raw]
Subject: Re: [PATCH] EVMS core (3/9) discover.c

On Thursday 10 October 2002 15:48, Andi Kleen wrote:
> Kevin Corry <[email protected]> writes:
> > +
> > + if (!gd) {
> > + gd = alloc_disk();
> > + BUG_ON(!gd);
>
> BUG_ON ? Can't this fail for legal reasons?

Yes, it can. This, and a couple other incorrect BUG_ON() statements have been
fixed to fail gracefully. Thanks for catching this.

--
Kevin Corry
[email protected]
http://evms.sourceforge.net/

2002-10-10 21:48:02

by Mark Peloquin

[permalink] [raw]
Subject: Re: [PATCH] EVMS core (3/9) discover.c


On 10/10/2002 at 03:48 PM, Andi Kleen wrote:

> > + list_for_each_entry(plugin, &plugin_head, headers) {
> > + if (GetPluginType(plugin->id) == EVMS_DEVICE_MANAGER) {
> > + spin_unlock(&plugin_lock);
> > + DISCOVER(plugin, disk_list);
> > + spin_lock(&plugin_lock);
> > + }

> How do you know "plugin" and its successors are still valid when retaking
> the spinlock? Looks like you need a reference count on the object here.

The spinlock itself should protect the integrity of the
list. If a prev or next element in the list should be
removed, while in a discover function, then the prev
or next field in the current plugin will get updated,
but I don't believe that should cause the list_for_each_entry
macro problems traversing the remainding elements in the list.

The first instruction in every plugin's discover function
is a MOD_INC_USE_COUNT and the last before the return is
MOD_DEC_USE_COUNT. So there exists a small window by
which the current plugin might be unloaded between the
spinlock release and MOD_INC_USE_COUNT, and the
MOD_DEC_USE_COUNT and the spinlock reacquire.

We plan to register a "__this_module.can_unload()" that
should prevent plugin modules from unloading during
discovery.

Mark


2002-10-10 22:13:17

by Andi Kleen

[permalink] [raw]
Subject: Re: [PATCH] EVMS core (3/9) discover.c

> We plan to register a "__this_module.can_unload()" that
> should prevent plugin modules from unloading during
> discovery.

Ok in this case. But how about when you search that list later after
discovery for some reason and drop the lock. Then you could race with someone
else removing the plugin inbetween, no ?

-Andi

2002-10-10 22:35:22

by Mark Peloquin

[permalink] [raw]
Subject: Re: [PATCH] EVMS core (3/9) discover.c


On 10/10/2002 at 05:19 PM, Andi Kleen wrote:

> > We plan to register a "__this_module.can_unload()" that
> > should prevent plugin modules from unloading during
> > discovery.

> Ok in this case. But how about when you search that list later after
> discovery for some reason and drop the lock. Then you could race with
someone
> else removing the plugin inbetween, no ?

The only time the lock is released while actively
searching the list is during discovery and direct
ioctl communication. So yes, you are correct, the
can_unload() would have to take both operations
into account. All other list operations take
place completely inside the lock. All in-use
plugins are kept from unloading by module ref
counts. Outside of the scope of the discover and
direct ioctl operations, any unused plugins
should be safe to unload.

Is something being missed?

Mark